Skip to content

Proposal: Native JWT API in Node. #62182

@nickshiro

Description

@nickshiro

What is the problem this feature will solve?

Summary

Introduce a built-in, zero-dependency JSON Web Token (JWT) implementation directly in the Node.js runtime, which supports signing, verification, and safe decoding.

Supported algorithms (minimum):

  • Symmetric: HS256, HS384, HS512
  • Asymmetric: RS256, RS384, RS512, ES256, ES384, ES512, EdDSA (Ed25519)

Goals:

  • significantly better performance compared to userland implementations.
  • reduced dependency on third-party cryptographic libraries.
  • direct integration with Node’s native crypto and Web Crypto APIs.

Motivation

JWT is one of the most widely used authentication mechanisms in HTTP servers, API gateways, serverless functions, and microservices.

Today, virtually every Node.js application depends on third-party libraries such as:

  • jsonwebtoken
  • jose
  • framework wrappers (fastify-jwt, express-jwt, etc.)

These libraries work well but introduce several issues:

  1. Performance overhead
    Most JWT libraries are implemented purely in JavaScript, resulting in:
  • unnecessary memory allocations
  • additional parsing overhead
  • slower cryptographic operations compared to native bindings
  1. Cold-start latency
    Serverless and edge environments benefit significantly from:
  • smaller dependency trees
  • less runtime initialization
  • fewer dynamic imports
    A native implementation reduces startup time and module loading cost.
  1. Security and dependency surface
    JWT is security-critical infrastructure. Depending on third-party packages introduces risks such as:
  • dependency vulnerabilities
  • supply-chain attacks
  • inconsistent verification defaults across libraries

Prior Art

Node.js already includes native primitives for common cryptographic operations via:

  • crypto
  • crypto.subtle (Web Crypto API)

Node has previously introduced high-level primitives built on top of crypto, such as:

  • crypto.sign()
  • crypto.verify()

Native JWT utility would follow a similar philosophy: provide safe, high-level primitives for extremely very common cryptographic workflows.

What is the feature you are proposing to solve the problem?

Proposed API

New jwt namespace under Node.js:

import { jwt } from "node:jwt";

API Design

jwt.sign()

namespace jwt {
  interface SignOptions {
    /**
     * Signing key.
     * string / Uint8Array for symmetric algorithms
     * CryptoKey or KeyObject for asymmetric algorithms
     */
    key: string | Uint8Array | CryptoKey | KeyObject;

    /**
     * Algorithm.
     * Defaults to HS256 when using string/Uint8Array keys
     */
    alg?:
      | "HS256"
      | "HS384"
      | "HS512"
      | "RS256"
      | "RS384"
      | "RS512"
      | "ES256"
      | "ES384"
      | "ES512"
      | "EdDSA";

    /**
     * Expiration time
     * seconds or duration string ("2h", "7d", "30m")
     */
    expiresIn?: number | string;

    /**
     * Not-before constraint
     */
    notBefore?: number | string;

    /**
     * Additional JOSE header fields
     */
    header?: Record<string, unknown>;

    /**
     * Issued-at timestamp
     * automatically set unless explicitly provided
     */
    iat?: number;
  }

  function sign(
    payload: object | string | Uint8Array,
    options: SignOptions
  ): Promise<string>;
}

Example

const token = await jwt.sign(
  { sub: "123" },
  { key: secret, expiresIn: "2h" }
);

jwt.verify()

namespace jwt {
  interface VerifyOptions {
    key: string | Uint8Array | CryptoKey | KeyObject;

    /**
     * Allowed algorithms
     */
    algorithms?: string[];

    /**
     * Allowed clock skew (seconds)
     */
    clockTolerance?: number;

    /**
     * Skip expiration validation
     */
    ignoreExpiration?: boolean;
  }

  interface JwtResult {
    payload: unknown;
    header: Record<string, unknown>;

    token?: {
      header: string;
      payload: string;
      signature: string;
    };
  }

  function verify(
    token: string,
    options: VerifyOptions
  ): Promise<JwtResult>;
}

Example

const result = await jwt.verify(token, {
  key: publicKey,
  algorithms: ["RS256"]
});

console.log(result.payload);

jwt.decode()

Decode a JWT without verifying the signature.

namespace jwt {
  interface DecodeOptions {
    complete?: boolean;
  }

  function decode(
    token: string,
    options?: DecodeOptions
  ):
    | JwtResult
    | { payload: unknown };
}

Example

const { payload } = jwt.decode(token);

What alternatives have you considered?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues that request new features to be added to Node.js.

    Type

    No type

    Projects

    Status

    Awaiting Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions