Signed Tokens

A lightweight JWT alternative: HMAC-SHA256 signed, purpose-bound, optionally expiring tokens. No algorithm-confusion attacks โ€” the algorithm is fixed, not user-controlled.

// Sign a structured payload
const token = await enc.sign(
  { userId: '123', role: 'admin' },
  'auth:access',
  { ttlSeconds: 3600 }
);
// โ†’ 'eyJhbGci....eyJ1c2VyS....ABC123...' (base64url, dot-separated)

// Verify โ€” never throws on invalid tokens (unless throwOnExpiry is set)
const result = await enc.verify<{ userId: string; role: string }>(token, 'auth:access');

if (result.valid) {
  console.log(result.payload);      // { userId: '123', role: 'admin' }
  console.log(result.remainingTtl); // seconds until expiry
} else {
  console.log(result.reason);       // 'expired' | 'invalid_signature' | 'malformed'
}

Throw on expiry

await enc.verify(token, 'auth:access', { throwOnExpiry: true });
// โ†’ throws EncryptixError { code: 'TOKEN_EXPIRED' }
sign<T>(payload: T, purpose: string, options?: SignTokenOptions): Promise<string>
verify<T>(token: string, purpose: string, options?: VerifyTokenOptions): Promise<VerifyTokenResult<T>>

VerifyTokenResult<T>:

type VerifyTokenResult<T> =
  | { valid: true;  payload: T; remainingTtl?: number }
  | { valid: false; reason: 'expired' | 'invalid_signature' | 'malformed' };
๐ŸŽซ

Tokens are bound to their purpose. A token signed for auth:access will not verify under auth:refresh โ€” no separate audience field required.