Crypto, JWT & Hashing
Tokens, password hashing, signing, encryption — what to actually use.
Use the platform first
- ★ Web Crypto API — built into Node 20+, Bun, Deno, Workers, browsers. Hashing, HMAC, AES-GCM, ECDSA, key generation, key derivation. Use this before reaching for a library.
crypto.randomUUID()— built-in UUID v4.crypto.subtle.digest('SHA-256', …)— hashing.crypto.subtle.importKey/sign/verify/encrypt/decrypt— full primitives.
JWT / JOSE
- ★ jose — modern JWT/JOSE library; Web Crypto-based; works on Node, Bun, Deno, Workers, browsers. The default.
- jsonwebtoken — older Node-only; works on Workers via shims; less recommended for new code.
- fast-jwt — high-performance Node-only.
@tsndr/cloudflare-worker-jwt— minimal Workers-friendly JWT.
Password hashing
- ★
@node-rs/argon2— Argon2id via Rust binding; the gold standard for password hashing. The default for new Node apps. @node-rs/bcrypt— Rust bcrypt; faster thanbcryptnpm.- bcrypt / bcryptjs — bcrypt is fine; bcryptjs is pure JS (slow but Workers-compatible).
- scrypt — built into Node's
crypto.scrypt. - argon2 (npm) — Node-only argon2 binding.
For Workers / edge runtimes that can't load native modules: use @noble/hashes for argon2/scrypt or move auth to a separate Node service.
Signed cookies / sessions
- ★ iron-webcrypto — Iron-encrypted blobs; works everywhere; pairs nicely with cookie helpers.
@hono/cookie, Next.jscookies()— framework-bundled cookie helpers.cookie-signature— older Node-only.
Random / IDs
- ★
crypto.randomUUID()— built-in UUID v4. Default. nanoid— short URL-safe IDs; smaller than UUID.uuid(npm) — full UUID library if you need v5/v7.@paralleldrive/cuid2— modern collision-resistant IDs.ulid— sortable IDs; great for DB primary keys.ksuid,xid— alternatives.
Modern crypto primitives
- ★
@noble/curves,@noble/hashes,@noble/ciphers— auditable, JS-only crypto. Use when Web Crypto doesn't have the algorithm you need (e.g., Ed25519 verify on older runtimes, BLS, secp256k1). scure-base— encodings (base58, base64, hex).- PASETO via
paseto(npm) — alternative to JWT; safer defaults. - JWE — for encrypted tokens; use jose.
- JOSE = JWS + JWE + JWA + JWK —
josecovers all of this.
TLS / certificate helpers
acme-client,greenlock— Let's Encrypt clients.- Caddy — server-level auto-HTTPS with built-in ACME.
- For the browser: WebCrypto plus your IdP — never hand-roll cert validation.
Don't roll your own
If you're tempted to implement password hashing, JWT signing, encryption, or session tokens by hand — don't. Use the libraries above. The list of bugs from hand-rolled crypto is endless.
Pick this if…
- Default token library: jose.
- Default password hashing:
@node-rs/argon2(Node) or argon2-via-@noble/hashes(Workers / Bun). - IDs in the DB:
crypto.randomUUID()(UUID v4) orulid(sortable). - Encrypted cookies / sessions: iron-webcrypto.
- Need a primitive Web Crypto doesn't have:
@noble/*.