Utilities
Small but pervasive helpers — base64url encoding, cryptographic randomness, type guards, JWT claim validation, and the Duration format that powers expiresIn / maxTokenAge / notBeforeIn everywhere in unjwt.
import {
base64UrlEncode,
base64UrlDecode,
base64Encode,
base64Decode,
secureRandomBytes,
concatUint8Arrays,
textEncoder,
textDecoder,
isJWK,
isJWKSet,
isSymmetricJWK,
isAsymmetricJWK,
isPrivateJWK,
isPublicJWK,
isCryptoKey,
isCryptoKeyPair,
validateJwtClaims,
computeDurationInSeconds,
inferJWSAllowedAlgorithms,
inferJWEAllowedAlgorithms,
sanitizeObject,
} from "unjwt/utils";
All of these are also available from the flat barrel unjwt.
Encoding
base64UrlEncode(data)
Encodes Uint8Array | string to Base64URL (no padding, URL-safe alphabet). Returns a string.
base64UrlEncode(new Uint8Array([255, 255])); // "__8"
base64UrlEncode("hello"); // "aGVsbG8"
base64UrlDecode(data, options?)
Decodes a Base64URL string or Uint8Array. Without an options object the return type follows the input: a string input returns a decoded string, a Uint8Array input returns a Uint8Array. Pass { returnAs: "string" } or { returnAs: "uint8array" } (alias "bytes") to force the return type.
base64UrlDecode("aGVsbG8"); // "hello"
base64UrlDecode("aGVsbG8", { returnAs: "uint8array" });
// Uint8Array([104, 101, 108, 108, 111])
base64UrlDecode(new Uint8Array([97, 71, 86, 115, 98, 71, 56]), { returnAs: "string" });
// "hello"
base64Encode(data) / base64Decode(data, options?)
Standard Base64 (with = padding). Same signatures as the URL-safe variants — base64Decode accepts the same { returnAs } options object as base64UrlDecode. Use when interoperating with systems that expect classical Base64.
Randomness
secureRandomBytes
secureRandomBytes(length);
Returns a cryptographically secure Uint8Array of the specified length, via crypto.getRandomValues:
const salt = secureRandomBytes(16); // 128-bit random salt
const cek = secureRandomBytes(32); // 256-bit CEK
Use this — not Math.random(), not a Node-specific API — for any security-sensitive randomness. It works everywhere unjwt runs.
randomBytes alias — use secureRandomBytes.Concatenation
concatUint8Arrays(...arrays)
Concatenates multiple Uint8Array instances into one. Useful when building custom envelopes or AAD strings without intermediate Buffer allocations:
const aad = concatUint8Arrays(
textEncoder.encode(base64UrlEncode(protectedHeader)),
new Uint8Array([0x2e]), // "."
textEncoder.encode(base64UrlEncode(externalAad)),
);
Singletons
textEncoder / textDecoder
Singleton TextEncoder and TextDecoder instances — reuse them instead of allocating new ones per call:
textEncoder.encode("hello"); // Uint8Array
textDecoder.decode(uint8ArrayBytes); // string
Type guards
| Function | Returns | Checks |
|---|---|---|
isJWK(key) | key is JWK | Object with kty |
isJWKSet(key) | key is JWKSet | Object with keys: JWK[] |
isCryptoKey(key) | key is CryptoKey | CryptoKey instance |
isCryptoKeyPair(key) | key is CryptoKeyPair | Object with publicKey + privateKey as CryptoKeys |
isSymmetricJWK(key) | key is JWK_oct | JWK with kty: "oct" and k property |
isAsymmetricJWK(key) | key is JWK_Asymmetric | JWK that isn't oct |
isPrivateJWK(key) | key is JWK_Private | Asymmetric JWK with a d (private component) |
isPublicJWK(key) | key is JWK_Public | Asymmetric JWK without d |
assertCryptoKey(key) | assertion | Throws if not a CryptoKey |
import { isPrivateJWK, isSymmetricJWK } from "unjwt/utils";
function canSign(k: unknown) {
return isSymmetricJWK(k) || isPrivateJWK(k);
}
JWT claim validation
validateJwtClaims
validateJwtClaims(claims, options?)
The low-level claim validator used inside verify() and decrypt(). Throws JWTError on failure. Checks:
exp— expirationnbf— not-beforeiat— issued-at type check and (withmaxTokenAge) age boundiss— issuersub— subjectaud— audiencerequiredClaims— presence of a named set
Timestamps (exp, nbf, iat) are validated strictly: if present but not a finite number (string, NaN, null), the check throws rather than silently skipping.
typ is part of {@link JWTClaimValidationOptions} but is validated at the verify() / decrypt() layer (against the protected header), not inside validateJwtClaims itself.JWTClaimValidationOptions
interface JWTClaimValidationOptions {
audience?: string | string[];
issuer?: string | string[];
subject?: string;
maxTokenAge?: MaxTokenAge; // alias of Duration
clockTolerance?: number; // seconds; defaults to 0
typ?: string;
currentDate?: Date;
requiredClaims?: string[];
/**
* Critical header parameters this caller understands.
* Verification fails if the token's `crit` header lists a parameter
* not in this list (RFC 7515 §4.1.11 / RFC 7516 §4.1.13).
*/
recognizedHeaders?: string[];
}
This same interface is extended by JWSVerifyOptions and JWEDecryptOptions — so the options below work identically everywhere.
The Duration format
expiresIn, notBeforeIn, maxTokenAge, and friends all accept a single type:
type Duration = number | `${number}` | `${number}${Unit}`;
// Unit short forms: s m h D W M Y
// Unit long forms: seconds minutes hours days weeks months years
// (both singular and plural accepted for long forms)
Number and unit must be adjacent — no space. Case matters for the single-letter day/week/month/year units: D, W, M, Y.
Examples that all resolve to 3600 seconds:
3600
"3600" "3600s" "3600seconds"
"60m" "60minutes"
"1h" "1hour"
And others:
"7D"; // 7 days
"1W"; // 1 week
"3M"; // 3 months (30-day months)
"1Y"; // 1 year (365-day years)
"30s"; // 30 seconds
computeDurationInSeconds(duration)
Converts a Duration to a positive integer number of seconds. Throws if the result is <= 0:
computeDurationInSeconds("1h"); // 3600
computeDurationInSeconds("7D"); // 604800
computeDurationInSeconds(30); // 30
computeMaxTokenAgeSeconds and computeExpiresInSeconds are @deprecated aliases retained for backward compatibility. Prefer computeDurationInSeconds in new code.Algorithm inference
Used internally to derive allowlists from key shapes when options.algorithms is omitted. Exposed for advanced use — e.g. building a logging layer that reports which algorithms your infrastructure actually exercises.
inferJWSAllowedAlgorithms
inferJWSAllowedAlgorithms(key);
Returns the set of signing algorithms a key can unambiguously produce, or undefined if inference isn't possible (raw bytes, alg-less JWK, lookup function).
inferJWEAllowedAlgorithms
inferJWEAllowedAlgorithms(key);
Returns the set of key-management algorithms a key can unambiguously handle. Passwords infer to the three PBES2-* variants plus "dir"; symmetric keys infer to their specific wrap alg plus "dir".
When these return undefined, callers must pass options.algorithms explicitly — otherwise verification / decryption has no effective algorithm guard.
sanitizeObject
sanitizeObject(obj);
Returns a deep structural copy of obj with the prototype-pollution vectors __proto__, prototype, and constructor stripped at every level. Input is never modified.
unjwt applies this internally to all parsed JWT headers and user-supplied options — you rarely need to call it yourself. Exposed for userland code that handles untrusted JSON.
See also
- Getting started: core concepts → — a plain-language walkthrough of the claims this module validates.
- JWS verifying → — the claim validation options in context.
- JWE decrypting → — same options on the encrypted side.