| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
JSON Web Token (JWT) encode/decode for Node. Zero runtime dependencies, timing-safe signature verification, synchronous result-object API.
npm install json-web-token
# or
pnpm add json-web-token
# or
yarn add json-web-tokenimport { encode, decode } from "json-web-token"; // ESM
// const { encode, decode } = require("json-web-token"); // CJS
const secret = "TOPSECRETTTTT";
const payload = { iss: "me", aud: "you", iat: Date.now() };
const { error, value: token } = encode(secret, payload);
if (error) throw error;
const { error: e2, value: decoded, header } = decode(secret, token);
if (e2) throw e2;
console.log(decoded, header);The library is synchronous — both encode and decode return their result immediately. If you want async ergonomics, wrap them yourself:
const tokenP = Promise.resolve(encode(secret, payload));const { value: token } = encode(secret, {
payload: { iss: "me", aud: "you" },
header: { kid: "my-key-id" },
}, "HS512");Header keys you provide are merged with the defaults — typ and alg are always set by the library and cannot be overridden through this surface.
const { error, value } = decode(publicKey, token, { algorithms: ["RS256"] });Any token whose header.alg is outside the list is rejected before any signature work happens.
function encode(
key: string | Buffer,
data: unknown,
algorithm?: string, // defaults to "HS256"
): EncodeResult;
function decode(
key: string | Buffer,
token: string,
options?: DecodeOptions,
): DecodeResult;
interface DecodeOptions {
algorithms?: string[]; // optional allowlist; rejects header.alg outside the list
}
function getAlgorithms(): string[]; // ["HS256","HS384","HS512","RS256"]
class JWTError extends Error { }EncodeResult is { error: JWTError | null; value: string | null }. DecodeResult is { error: JWTError | null; value: unknown; header?: JWTHeader }.
HS256, HS384, HS512, RS256.
The { error, value, [header] } return shape and getAlgorithms() / JWTError are unchanged. Callback overloads have been removed — v4 is sync-only. If you used the callback form in v3:
// v3
jwt.encode(secret, payload, (err, token) => { ... });
// v4 — just inline it
const { error, value: token } = jwt.encode(secret, payload);
if (error) { /* ... */ }Other changes worth knowing:
| Topic | v3 | v4 |
|---|---|---|
| Min Node | >=8 | >=18 |
| Runtime deps | 4 (base64-url, is.object, json-parse-safe, xtend) | none |
| Call style | callback OR result-object | result-object only |
| HMAC verify | string === (timing-leaky) | crypto.timingSafeEqual |
| Algorithm confusion | vulnerable (CVE-2023-48238) | fixed — key-type / alg-family guard on encode + decode |
| Algorithm allowlist | none | optional algorithms in decode options |
| Module formats | CJS only | ESM + CJS via exports map |
| Types | hand-written index.d.ts (loose any) | TS source, generated .d.mts / .d.cts |
| Base64url | base64-url package | Node native Buffer |
| Build | hand-edited index.js | tsup from TS |
| Test runner | mocha + nyc | vitest with v8 coverage |
| Linter | standard | biome |
| CI | Travis (Node 8/10/12) | GitHub Actions (Node 20/22/24) |
Zero runtime dependencies. What ships in the npm tarball:
| What | Raw | Gzipped |
|---|---|---|
| ESM runtime (index.mjs) | 4 880 B | 1 599 B |
| CJS runtime (index.cjs) | 4 941 B | 1 609 B |
| Types (.d.mts / .d.cts) | 5 238 B | 1 167 B |
| Sourcemaps (debug-only, not loaded) | 27 610 B | 4 511 B |
Only one of the two runtime files is loaded by your bundler / Node, so the real cost in your app is ~1.6 kB gzipped.
ISC © @joaquimserafim
| Back | FazBrowse Home | New Git URL |