[READ-ONLY] Mirror of https://github.com/sandros94/unjwt. A collection of JWT utilities based on the Web Crypto API, in a single zero-depend unjwt.s94.dev
auth browser decrypt edge encrypt jose jwe jwk jws jwt security serverless session sign verify webcrypto

README.md

unjwt #

npm version npm downloads bundle size

A collection of JWT utilities based on the Web Crypto API, in a single zero-dependency package.

📖 Documentation — unjwt.s94.dev

Features #

  • JWS (RFC 7515): HMAC, RSA, RSA-PSS, ECDSA, EdDSA. Compact and General/Flattened JSON serialization (several signatures over one payload).
  • JWE (RFC 7516): dir, AES-KW, AES-GCM-KW, RSA-OAEP, PBES2, ECDH-ES, with AES-GCM and AES-CBC-HMAC-SHA2 content encryption. Compact and General/Flattened JSON serialization (several recipients for one ciphertext).
  • JWK (RFC 7517): generate, import, export, wrap and unwrap keys; CryptoKey, JWK and PEM conversion; thumbprints as key IDs; JWK Set lookup and rotation.
  • DPoP (RFC 9449): create and verify proofs with unjwt/dpop. Your application owns HTTP and replay storage.
  • Sessions: signed or encrypted cookie sessions with unjwt/session over a Web Request, on h3, Nitro, Nuxt, Hono and Workers, Bun and Deno, or on a token in hand; a driver for any other framework.
  • One typed error: every failure is a JWTError with a code to branch on.
  • Runtime-agnostic: Node.js 22+, Bun, Deno, Cloudflare Workers and browsers, anywhere Web Crypto runs.
  • Zero runtime dependencies, no peer dependencies.

Install #

# Auto-detect package manager (npm, yarn, pnpm, deno, bun)
npx nypm install unjwt

Agent skill for Claude Code (and compatible harnesses):

npx skills add sandros94/unjwt

Quickstart #

// Sign with the private half, verify with the public one (JWS)
import { sign, verify } from "unjwt/jws";
import { generateJWK } from "unjwt/jwk";

const { privateKey, publicKey } = await generateJWK("ES256");
const token = await sign({ sub: "user_1a2b" }, privateKey, { expiresIn: "15m" });
const { payload } = await verify(token, publicKey);
// { sub: "user_1a2b", iat: …, exp: … }

// Encrypt for a recipient's public key, decrypt with its private key (JWE)
import { encrypt, decrypt } from "unjwt/jwe";

const recipient = await generateJWK("ECDH-ES+A256KW");
const jwe = await encrypt({ invoice: "inv_2026_0042" }, recipient.publicKey);
const opened = await decrypt(jwe, recipient.privateKey);

// Keep a session in a cookie on any Web Request
import { defineJWESession } from "unjwt/session";

const appSession = defineJWESession({ key: await generateJWK("A256GCM"), maxAge: "7D" });
const headers = new Headers();
const session = await appSession.use({ req: request, res: { headers } });
await session.update({ user: "user_1a2b" }); // Set-Cookie lands on `headers`

Walkthroughs, recipes and the full API live at unjwt.s94.dev:

Development #

local development
  • Clone this repository
  • Install latest LTS version of Node.js
  • Enable Corepack using corepack enable
  • Install dependencies using pnpm install
  • Run tests using pnpm test

Credits #

Originally visioned by Johann Schopplich, heavily inspired by Filip Skokan's jose internal cryptographic primitives, and initially sponsored by JAMflow — thanks to all three for making this project possible.

License #

Published under the MIT license. Made by community 💛


🤖 auto updated with automd