Post-Quantum JWT Library/Package for Node.js/JS/TypeScript (NIST FIPS 204M-DSA)

bchain1 pts1 comments

@pq-jwt/core - npm

npm

Search<br>Sign UpSign In

@pq-jwt/core

1.0.6 • Public • Published 18 hours ago<br>Readme<br>Code Beta<br>2 Dependencies<br>0 Dependents<br>7 Versions<br>PQ-JWT - Universal Post-Quantum JWT Library

A comprehensive, production-ready JavaScript/Node.js library for generating, managing, signing, and verifying Post-Quantum Cryptography (PQC) JSON Web Tokens (JWTs) . It acts as a drop-in successor to broken RS256/ES256 libraries.

This library provides quantum-resistant JWT authentication using pure Javascript implementations of NIST-standardized algorithms via the highly audited @noble/post-quantum package.

Ecosystem

Package<br>Description

@pq-jwt/core<br>This library — sign, verify, ML-DSA, SLH-DSA

PQ-JWT-Demo<br>Full demo — Express, MongoDB, TypeScript, UI

Author

Sachin Ruhil · github.com/ruhil6789

npm: npmjs.com/~ruhil6789

Features

Zero Native Dependencies : Uses pure JS math, running seamlessly in Node.js >= 20.19 without complex C++ bindings.

Post-Quantum Ready : Implements NIST-standardized ML-DSA (Dilithium) and SLH‑DSA (SPHINCS+) signature algorithms.

Familiar API : Syntax identical to classic JWT libraries (sign(), verify(), decode(), refresh()).

Standard JWT Claims : Automatic validation of exp (with duration strings like '1h'), nbf, iss, sub, and aud.

Comprehensive Error Handling : Typed exceptions (TokenExpiredError, SignatureError, InvalidTokenError).

Key Serialization : Export and import keys effortlessly via fast hex encoding.

TypeScript Support : Full .d.ts types included out of the box.

Requirements

Node.js 20.19.0 or higher

Installation

npm

npm install @pq-jwt/core

yarn

yarn add @pq-jwt/core

pnpm

pnpm add @pq-jwt/core

Quick Start

Basic Sign & Verify

import { generateKeyPair, sign, verify } from "@pq-jwt/core";

// 1. Generate keys (uses ML-DSA-65 by default)<br>const { publicKey, secretKey } = generateKeyPair("ML-DSA-65");

// 2. Sign a JWT payload<br>const jwtToken = sign({ userId: 123, role: "admin" }, secretKey, {<br>expiresIn: "1h",<br>issuer: "my-app",<br>});<br>console.log("Generated JWT:", jwtToken);

// 3. Verify the token<br>try {<br>const decoded = verify(jwtToken, publicKey, { issuer: "my-app" });<br>console.log("JWT is valid!");<br>console.log("Payload:", decoded.payload);<br>} catch (error) {<br>console.log("Verification failed:", error.message);

Token Refresh (Sliding Sessions)

import { refresh } from "@pq-jwt/core";

// Automatically verifies the old token, bumps the iat and exp, and resigns<br>const newToken = refresh(oldToken, publicKey, secretKey, { expiresIn: "1h" });

Supported Algorithms

ML-DSA (Dilithium) - NIST FIPS 204

Algorithm<br>Security Level<br>Quantum Security<br>Description

ML-DSA-44<br>Level 2<br>64-bit Q<br>IoT / constrained environments

ML-DSA-65<br>Level 3<br>96-bit Q<br>General use (DEFAULT)

ML-DSA-87<br>Level 5<br>128-bit Q<br>High security / Government

SLH‑DSA (SPHINCS+) - NIST FIPS 205

Algorithm<br>Security Level<br>Quantum Security<br>Description

SLH-DSA-SHA2-128s<br>Level 1<br>64-bit Q<br>Conservative / Archival

Note: SPHINCS+ produces much larger signatures and is slower to compute, but relies on conservative hash-based assumptions rather than lattice cryptography.

Key Management & Environment Variables (.env)

One of the biggest confusions for developers switching to Post-Quantum JWTs is how to handle the massive keys. Classic JWTs (like HS256) use a simple text password (e.g., JWT_SECRET="my-super-secret") that you can easily put in your .env file.

Because ML-DSA and SLH-DSA use asymmetric cryptography , their keys are massive, mathematically-linked byte arrays (often 4KB+). You cannot generate these keys dynamically every time your server starts, or all previously issued tokens will instantly become invalid.

You handle this by generating the keys ONCE, converting them to hex, and placing them in your .env file!

Step 1: One-Time Setup Script

Run this script locally on your machine just once to generate your public and private keys, and encode them into pure strings using exportKey.

// one-time-setup.mjs<br>import { generateKeyPair, exportKey } from "@pq-jwt/core";

// Generate keys once<br>const { publicKey, secretKey } = generateKeyPair("ML-DSA-65");

// Convert huge byte arrays to safe hexadecimal strings<br>console.log("--- ADD THESE TO YOUR .env FILE ---");<br>console.log(`PQ_PRIVATE_KEY="${exportKey(secretKey)}"`);<br>console.log(`PQ_PUBLIC_KEY="${exportKey(publicKey)}"`);

Step 2: Production Server

In your production app (like Express or Next.js), you simply read those strings from your .env file and convert them back into crypto-ready keys using importKey. This makes your workflow absolutely identical to classic JWTs!

// server.mjs<br>import { importKey, sign, verify } from "@pq-jwt/core";

// 1. Load keys securely from Environment Variables!<br>const secretKey = importKey(process.env.PQ_PRIVATE_KEY);<br>const publicKey = importKey(process.env.PQ_PUBLIC_KEY);

// 2. Sign and Verify normally!<br>const token = sign({ userId: 123 }, secretKey, { expiresIn: "1h" });<br>const payload = verify(token, publicKey);

This pattern...

core sign keys quantum verify const

Related Articles