DEV Community

Cover image for Why You Should Delete jsonwebtoken in 2025 ⭐
Ali nazari
Ali nazari

Posted on

Why You Should Delete jsonwebtoken in 2025 ⭐

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
Enter fullscreen mode Exit fullscreen mode

🧓 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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

2. Replace verify

// jsonwebtoken
jwt.verify(token, secret)

// jose
const { payload } = await jwtVerify(token, secret)
Enter fullscreen mode Exit fullscreen mode

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!!: 🤝

LinkedIn
GitHub

Top comments (2)

Collapse
 
abrar_ahmed profile image
Abrar ahmed

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?

Collapse
 
silentwatcher_95 profile image
Ali nazari

thanks !
you can use env vars as pointers (not stores), and lean on your cloud KMS if you can.