Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

json-web-token

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-web-token

JSON Web Token (JWT) encode/decode for Node — zero runtime dependencies, timing-safe verification.

latest
Source
npmnpm
Version
4.0.1
Version published
Weekly downloads
5.5K
-13.68%
Maintainers
1
Weekly downloads
 
Created
Source

json-web-token

CI npm version

JSON Web Token (JWT) encode/decode for Node. Zero runtime dependencies, timing-safe signature verification, synchronous result-object API.

Install

npm install json-web-token
# or
pnpm add json-web-token
# or
yarn add json-web-token

Quick start

import { 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));

Custom headers

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.

Locking decode to a specific algorithm

const { error, value } = decode(publicKey, token, { algorithms: ["RS256"] });

Any token whose header.alg is outside the list is rejected before any signature work happens.

API

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 }.

Security notes

  • CVE-2023-48238 (algorithm confusion) is fixed. v4 refuses to verify any token whose algorithm family does not match the key handed to decode. PEM-encoded keys (anything starting with -----BEGIN) can only be paired with the asymmetric algorithms (RS*); plain secrets (string or Buffer without PEM markers) can only be paired with the HMAC algorithms (HS*). This blocks the classic RS256→HS256 swap where an attacker re-signs a token with HMAC using the server's RSA public key as the HMAC secret.
  • Optional algorithm allowlist. Safety-conscious callers can pass { algorithms: ["RS256"] } (or any subset) to decode to reject any token whose header.alg is outside that list, in addition to the key-type guard above.
  • Timing-safe HMAC verify — v4 compares signatures with crypto.timingSafeEqual on length-checked Buffers, removing the timing side-channel that was present in v3's string === compare.
  • alg: 'none' is rejected in both encode and decode.
  • Claim validation is out of scope. exp, nbf, iat, iss, aud, sub are not validated automatically. Check them in your own code on the decoded payload.

Supported algorithms

HS256, HS384, HS512, RS256.

Migrating from v3

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:

Topicv3v4
Min Node>=8>=18
Runtime deps4 (base64-url, is.object, json-parse-safe, xtend)none
Call stylecallback OR result-objectresult-object only
HMAC verifystring === (timing-leaky)crypto.timingSafeEqual
Algorithm confusionvulnerable (CVE-2023-48238)fixed — key-type / alg-family guard on encode + decode
Algorithm allowlistnoneoptional algorithms in decode options
Module formatsCJS onlyESM + CJS via exports map
Typeshand-written index.d.ts (loose any)TS source, generated .d.mts / .d.cts
Base64urlbase64-url packageNode native Buffer
Buildhand-edited index.jstsup from TS
Test runnermocha + nycvitest with v8 coverage
Linterstandardbiome
CITravis (Node 8/10/12)GitHub Actions (Node 20/22/24)

Size

Zero runtime dependencies. What ships in the npm tarball:

WhatRawGzipped
ESM runtime (index.mjs)4 880 B1 599 B
CJS runtime (index.cjs)4 941 B1 609 B
Types (.d.mts / .d.cts)5 238 B1 167 B
Sourcemaps (debug-only, not loaded)27 610 B4 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.

License

ISC © @joaquimserafim

Keywords

jwt

FAQs

Package last updated on 27 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts