A minimal, secure, and production-ready JWT (JSON Web Token) library for Node.js with zero dependencies. Supports all standard signing algorithms (HMAC, RSA, ECDSA, EdDSA, RSASSA-PSS), automatic algorithm detection, JWK/JWKS, and full claim validation.
β¨ Why another JWT library? Most JWT libraries are bloated, have security pitfalls, or lack proper TypeScript support. This library is:
- Tiny
- Secure by default
- TypeScript-first with full JSDoc
- Zero external dependencies
- 100% test coverage (Tryingπ)
- Dual API: Sync and Promise-based
- Automatic algorithm detection based on key type
- Full JWK/JWKS support (
import/export,toPublicJWK,x5c/x5t, RFC 7638 thumbprints, kid-based key selection)
npm install @sourceregistry/node-jwtRequires Node.js β₯ 16
import { sign, verify, decode } from '@sourceregistry/node-jwt';
// Sign (algorithm auto-detected)
const token = sign(
{ sub: '1234567890', name: 'John Doe', iat: Math.floor(Date.now() / 1000) },
'your-secret-key'
);
// Verify
const result = verify(token, 'your-secret-key', { issuer: 'https://example.com' });
if (result.valid) {
console.log('Payload:', result.payload);
} else {
console.error('JWT Error:', result.error.code, result.error.reason);
}
// Decode (unsafe)
const { header, payload, signature } = decode(token);import { sign, verify, decode } from '@sourceregistry/node-jwt/promises';
// Sign (algorithm auto-detected)
const token = await sign(
{ sub: '1234567890', name: 'John Doe', iat: Math.floor(Date.now() / 1000) },
'your-secret-key'
);
// Verify
try {
const { payload } = await verify(token, 'your-secret-key', {
issuer: 'https://example.com',
audience: 'my-app',
algorithms: ['HS256']
});
console.log('Payload:', payload);
} catch (error) {
console.error('JWT Error:', error.code, error.reason);
}
// Decode (unsafe)
const { header, payload, signature } = await decode(token);When options.alg is omitted, the library automatically selects the correct JWT algorithm based on the signing key.
| Key Type | Detection Logic | Selected Algorithm |
|---|---|---|
Symmetric (string / Buffer) |
Default HMAC | HS256 |
| RSA private key | PKCS#1 v1.5 | RS256 |
| RSA-PSS private key | Hash algorithm in key | PS256 / PS384 / PS512 |
EC P-256 (prime256v1) |
Curve name | ES256 |
EC P-384 (secp384r1) |
Curve name | ES384 |
EC P-521 (secp521r1) |
Curve name | ES512 |
| EC secp256k1 | Curve name | ES256K |
| Ed25519 | Key type | EdDSA |
π‘ Node.js exposes OpenSSL curve names (
prime256v1,secp384r1, etc.). These are automatically normalized to JOSE algorithms.
Autodetection fails for unsupported keys:
- Unsupported EC curve
- Unsupported RSA-PSS hash algorithm (e.g.
sha1) - Unsupported asymmetric key type (e.g. DSA)
| Algorithm | Type | Secret Type |
|---|---|---|
| HS256 / HS384 / HS512 | HMAC | string | Buffer |
| RS256 / RS384 / RS512 | RSA | Private / Public key |
| PS256 / PS384 / PS512 | RSA-PSS | Private / Public key |
| ES256 / ES384 / ES512 | ECDSA | Private / Public key |
| ES256K | ECDSA (secp256k1) | Private / Public key |
| EdDSA | Ed25519 | Private / Public key |
Keys may be PEM, DER, JWK, or Node.js
KeyObject.
- Import/export JWK:
importJWK(),exportJWK() - Convert to public-only JWK:
toPublicJWK() - Compute RFC 7638 thumbprint:
getJWKThumbprint() - Support x5c/x5t (X.509 cert chain + SHA-1 thumbprint)
- Normalize JWKS with auto-generated
kidandx5t - Resolve keys from JWKS by
kidfor verification
import { JWKS, JWK } from '@sourceregistry/node-jwt';
const keyPair = generateKeyPairSync('rsa', { modulusLength: 2048 });
const jwk = JWK.toPublic(keyPair.publicKey);
const jwks = JWKS.normalize({ keys: [jwk] });
// Retrieve key by kid
const keyObject = JWKS.toKeyObject(jwks, jwk.kid);- β Safe algorithm autodetection
- β
Strict algorithm whitelisting (
algorithmsoption) - β Full RSASSA-PSS and Ed25519 support
- β
Time claim validation (
exp,nbf,iat) with clock skew - β
Claim validation (
iss,sub,aud,jti) - β Maximum token age enforcement
- β Timing-safe signature comparison
- β No insecure defaults
For ECDSA algorithms (ES256, ES384, ES512, ES256K) there are two common signature encodings:
- DER (ASN.1) β what Node.js produces by default
- JOSE (
r || sraw signature) β required by the JWT/JWS spec and used by systems like VAPID/Web Push (WNS)
By default, this library outputs DER signatures for ES* algorithms to match Node.js/OpenSSL defaults.
To generate spec-compliant JOSE ECDSA signatures, set:
signatureFormat: "jose"insign()
import { sign, verify } from "@sourceregistry/node-jwt";
const token = sign(
{ sub: "123", iat: Math.floor(Date.now() / 1000) },
ecPrivateKey,
{ alg: "ES256", signatureFormat: "jose" }
);
// Verify JOSE-signed token
const result = verify(token, ecPublicKey, { signatureFormat: "jose" });If enabled in your version, verify() can also validate JOSE ECDSA signatures without specifying signatureFormat (it will try DER first, then JOSE).
If you want strict behavior, pass signatureFormat: "der" or signatureFormat: "jose" explicitly.
π‘ For VAPID/Web Push (e.g. Windows WNS endpoints), you typically need
ES256withsignatureFormat: "jose".
alg(optional) β If omitted, algorithm is auto-detectedkidβ Key IDtypβ Token type (default:"JWT")
Includes algorithm whitelist protection and full claim validation.
Error Codes include:
INVALID_TOKENINVALID_ALGORITHMALGORITHM_NOT_ALLOWEDINVALID_SIGNATURETOKEN_EXPIREDTOKEN_NOT_ACTIVETOKEN_TOO_OLDMISSING_*/INVALID_*
Decode a JWT without verification (unsafe).
- 100% branch coverage
- All algorithms + autodetection paths
- All failure modes
- Sync + Promise APIs
- Full JWK/JWKS coverage (import/export, x5c/x5t, thumbprint, kid selection)
npm test
npm run test:coverage| Import | Description |
|---|---|
@sourceregistry/node-jwt |
Sync API |
@sourceregistry/node-jwt/promises |
Promise API |
PRs welcome! Please add tests and maintain full coverage.
π Security issues? Report responsibly: a.p.a.slaa@projectsource.nl
π GitHub: https://github.com/SourceRegistry/node-jwt π¦ npm: https://www.npmjs.com/package/@sourceregistry/node-jwt