token
js/security/token.ts
fino:security/token - HMAC-signed opaque JSON tokens.
This module issues compact payload.signature tokens for workflows that
need tamper-evident JSON payloads without the full JWT/JWK stack. Tokens may
include a purpose marker and expiration timestamp, and verification returns
null for malformed, expired, mismatched, or tampered input.
Tokens are authenticated but not encrypted. Anyone holding a token can decode the JSON payload, so keep secrets out of it. Use separate purposes for session, email verification, and password-reset flows to prevent accidental token reuse across workflows.
import { issueToken, verifyToken } from 'fino:security/token';
const token = issueToken({ sub: 'user-123' }, tokenSecret, {
purpose: 'email-verify',
expiresIn: 900,
});
const payload = verifyToken(token, tokenSecret, { purpose: 'email-verify' });
Interfaces
interface IssueTokenOptions {
Options for issuing an HMAC-signed opaque JSON token.
Tokens are authenticated but not encrypted: anyone holding the token can
base64url-decode and read the JSON payload. Use purpose to bind tokens to a
workflow, and expiresIn to add an expiration claim.
import type { IssueTokenOptions } from 'fino:security/token';
const options: IssueTokenOptions = { purpose: 'email-verify', expiresIn: 900 };
Properties
expiresIn?: number
Lifetime in seconds from issuance time.
When omitted, no exp claim is added and verification will not expire the
token. Negative values create already-expired tokens.
import type { IssueTokenOptions } from 'fino:security/token';
const options: IssueTokenOptions = { expiresIn: 60 };
purpose?: string
Optional purpose marker embedded in the payload.
Verification can require the same purpose to prevent a token issued for one flow from being reused in another.
import type { IssueTokenOptions } from 'fino:security/token';
const options: IssueTokenOptions = { purpose: 'password-reset' };
interface VerifyTokenOptions {
Options for verifying an HMAC-signed opaque JSON token.
Verification checks the HMAC signature, optional expiration, and optional
purpose. Invalid, expired, or mismatched tokens return null rather than
throwing.
import type { VerifyTokenOptions } from 'fino:security/token';
const options: VerifyTokenOptions = { purpose: 'email-verify', clockTolerance: 30 };
Properties
purpose?: string
Required purpose value.
When provided, the token payload must contain the same purpose string or
verification returns null.
import type { VerifyTokenOptions } from 'fino:security/token';
const options: VerifyTokenOptions = { purpose: 'password-reset' };
now?: number
Current Unix time in seconds.
Defaults to Date.now() / 1000. Supplying it is useful for tests or
replaying verification at a known time.
import type { VerifyTokenOptions } from 'fino:security/token';
const options: VerifyTokenOptions = { now: 1_700_000_000 };
clockTolerance?: number
Expiration grace period in seconds.
Defaults to 0. The value is added to exp during verification to allow
small clock skews.
import type { VerifyTokenOptions } from 'fino:security/token';
const options: VerifyTokenOptions = { clockTolerance: 30 };
Functions
function issueToken(
payload: Record<string, unknown>,
secret: BufferLike,
options: IssueTokenOptions = {
}
): string
Issue a signed opaque JSON token as payload.signature.
The payload is JSON-serialized and base64url-encoded, then signed with HMAC-SHA-256. The returned token is tamper-evident but not encrypted, so do not include secrets in the payload.
import { issueToken } from 'fino:security/token';
const token = issueToken({ sub: 'user-123' }, 'secret', {
purpose: 'session',
expiresIn: 3600,
});
function verifyToken(
token: string,
secret: BufferLike,
options: VerifyTokenOptions = {
}
): Record<string, unknown> | null
Verify a signed opaque JSON token and return its JSON payload.
Returns null for malformed tokens, signature mismatches, expired tokens,
JSON decode failures, or purpose mismatches. Signature comparison is
timing-safe. The returned payload includes any purpose or exp fields that
were embedded during issuance.
import { issueToken, verifyToken } from 'fino:security/token';
const token = issueToken({ sub: 'user-123' }, 'secret', { purpose: 'session' });
const payload = verifyToken(token, 'secret', { purpose: 'session' });