Enforced invariants
- Verification pins one configured algorithm; token headers never choose the allowlist.
alg: none, unknownkid, bad signatures, wrong issuer/audience, malformed claims, and missing required claims are rejected.- Access tokens are short-lived and require
iss,aud,sub,jti,iat,nbf, andexp. - Refresh secrets are high entropy, SHA-256 hashed at rest, rotated on use, and retained as tombstones for replay detection.
- Telemetry records guard, key ID, and outcome only. Raw tokens and personally identifiable data are forbidden in events, logs, and spans.
Production checklist
- Store private PEMs, HS256 secrets, database credentials, and encryption keys in a secret manager.
- Keep JWT signing keys separate from
APP_KEYand rotate both using independent procedures. - Use Ed25519 or another asymmetric algorithm unless all verifiers are equally trusted to mint.
- Use HTTPS, strict CORS, and
securecookies. Never place bearer or refresh tokens in URLs. - Keep access TTLs in minutes. Set refresh TTLs to the session lifetime the product actually needs.
- Rate-limit login and refresh endpoints. Alert on refresh reuse and repeated authentication failure.
- Use a shared cache denylist when logout must revoke access immediately across multiple instances.
- Back up the encrypted signing-key table with the encryption key ring required to decrypt it.
- Treat any token copied into logs, tickets, analytics, or crash reports as compromised.
Stable errors
Package exceptions render { errors: [{ code, message }] }. Relevant codes include
E_TOKEN_EXPIRED, E_TOKEN_REVOKED, E_INVALID_REFRESH_TOKEN,
E_REFRESH_TOKEN_REUSE, E_CSRF_TOKEN_MISMATCH, and E_JWT_CONFIG.
Do not return parser or JOSE internals to clients.
Events and traces
Subscribe to the typed events jwt:login, jwt:authenticated, jwt:authentication_failed,
jwt:refreshed, jwt:refresh_reuse_detected, jwt:logout, and jwt:key_rotated. The package
creates jwt.issue, jwt.verify, jwt.refresh, and jwt.reuse_detected OpenTelemetry spans.
Incident response
- Leaked access token: add its
jtito a shared denylist untilexp; investigate the source. - Leaked refresh token: revoke the token family or all refresh tokens for the user; require login.
- Signing key compromise: introduce and promote a new database key, remove the compromised key from verification immediately if active forgery risk outweighs breaking live access tokens, then revoke affected refresh sessions.
- Refresh reuse alert: the family is already revoked by the package. Invalidate the client session, notify the user when appropriate, and review IP/device evidence outside token telemetry.
- Database or encryption-key compromise: assume refresh hashes and encrypted private material are exposed; rotate signing and encryption keys and invalidate sessions according to risk.
Report package vulnerabilities using the private process in the repository SECURITY.md, not a
public issue.