Something went wrong. Try again.
[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
Something went wrong. Try again.
Files
- TypeScript 100%
- HTML <1%
- Vue <1%
README.md
unjwt #
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/sessionover a WebRequest, 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
JWTErrorwith acodeto 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:
- Getting started →
- Sign and verify (JWS) →
- Encrypt and decrypt (JWE) →
- Keys (JWK) →
- Sessions →
- Safety: errors, claims and limits →
- DPoP proofs →
- Recipes →: sessions on h3, Nitro, Nuxt, Hono and Workers, CDN imports, custom drivers.
- Migration guides →
Development #
local development
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