For years, if you wanted to sign and verify JWTs in Node.js, your go-to library was jsonwebtoken
. It’s simple, battle-tested, and still widely used.
But 2025 is not 2015.
Security standards have evolved, so have our build tools, JavaScript runtimes, and cryptographic best practices. If you're still using jsonwebtoken
, you're missing out on a faster, more secure, and fully modern alternative: jose
.
This post will explain why jose
should be your new default and what makes it more suitable for modern development.
What Is jose
?
jose
is a modern implementation of the JOSE (JavaScript Object Signing and Encryption) standards. It supports:
- JSON Web Tokens (JWT)
- JSON Web Keys (JWK)
- JSON Web Signature (JWS)
- JSON Web Encryption (JWE)
…and more, all using native Web Crypto APIs and with full support for Node.js, browsers, TypeScript, and ESM.
Unlike jsonwebtoken
, jose
is not a legacy library. It's actively maintained, security-focused, and ready for the current JavaScript ecosystem.
The Problems with jsonwebtoken
While jsonwebtoken
works, it comes with serious limitations:
1. No ESM Support
If you're using ESM-only runtimes or bundlers (e.g., Vite, Bun, Deno), jsonwebtoken
simply doesn't work out of the box.
2. Outdated Cryptography
It relies on older crypto implementations and doesn't support modern algorithms like EdDSA or RSA-PSS.
3. Poor Maintenance
While still popular, development is slow. Security patches happen, but new features are rare.
4. CommonJS + Callbacks
The library still uses legacy patterns like callbacks and CommonJS modules, which don’t align with modern JavaScript projects.
Why jose
Is the Better Choice
Here’s what makes jose
stand out:
✅ Modern Algorithm Support
Supports a wide range of cryptographic algorithms:
- HMAC (
HS256
,HS512
) - RSA (
RS256
,PS512
) - ECDSA (
ES256
,ES512
) - EdDSA (
Ed25519
)
This gives you flexibility to adopt the best option for your use case.
✅ First-Class TypeScript
Built with TypeScript from the ground up. You get intelligent autocompletion, type safety, and better DX.
✅ Fully ESM-Compatible
Seamless integration with modern bundlers and runtimes.
✅ Works in the Browser
Need to validate or sign a JWT on the frontend? jose
works in the browser with the same API.
✅ Actively Maintained
Created by panva, a trusted maintainer in the auth/crypto ecosystem. You can expect fast updates and solid security.
Example: Signing and Verifying Tokens
jose
(Modern Way)
import { SignJWT, jwtVerify } from 'jose'
const secret = new TextEncoder().encode('super-secret')
const token = await new SignJWT({ userId: 42 })
.setProtectedHeader({ alg: 'HS256' })
.setExpirationTime('1h')
.sign(secret)
const { payload } = await jwtVerify(token, secret)
console.log(payload.userId) // 42
🧓 jsonwebtoken
(Legacy Way)
import jwt from 'jsonwebtoken'
const token = jwt.sign({ userId: 42 }, 'super-secret', { expiresIn: '1h' })
const payload = jwt.verify(token, 'super-secret')
console.log(payload.userId) // 42
Migrating from jsonwebtoken
to jose
You can replace your usage in 2 steps:
1. Replace sign
// jsonwebtoken
jwt.sign(payload, secret, options)
// jose
await new SignJWT(payload)
.setProtectedHeader({ alg: 'HS256' })
.setExpirationTime('1h')
.sign(secret)
2. Replace verify
// jsonwebtoken
jwt.verify(token, secret)
// jose
const { payload } = await jwtVerify(token, secret)
You’ll also need to convert your secret to a Uint8Array
using TextEncoder
.
Real-World Use Cases for jose
- Secure API authentication with JWTs
- Stateless session tokens
- OAuth2/OpenID Connect providers
- Microservice-to-microservice token validation
- Web Crypto-compatible browser JWT validation
When You Might Still Use jsonwebtoken
- You're working in a legacy CommonJS project.
- You only need simple JWT signing and verification and don’t want to refactor.
- You're limited to environments where installing
jose
isn't feasible.
Even then, it’s wise to plan a migration eventually.
TL;DR
Feature | jose |
jsonwebtoken |
---|---|---|
TypeScript Support | ✅ Native | ☑️ Partial |
ESM Support | ✅ Yes | ❌ No |
Browser Compatibility | ✅ Yes | ❌ No |
Algorithm Support | ✅ Broad (EdDSA, ES, PS, etc.) | ⚠️ Limited |
Maintenance | ✅ Active (Panva) | 💤 Slow |
Crypto Security | ✅ Uses native Web Crypto API | ⚠️ Custom crypto |
API Style | ✅ Promise-based, functional | ❌ Callback/Sync |
Use jose
if you're building modern apps.
Keep jsonwebtoken
if you're stuck with legacy code — but plan to migrate.
If you found this helpful, feel free to share or comment with your JWT migration experience!
Let’s connect!!: 🤝
Top comments (2)
This is a great breakdown! It’s true—jsonwebtoken was a lifesaver in its day, but jose is the future.
Love how jose leverages Web Crypto + TypeScript-first design.
Curious: Any tips for handling secret management (e.g., rotation) when migrating to jose?
thanks !
you can use env vars as pointers (not stores), and lean on your cloud KMS if you can.