Why jwt.verify Doesn’t Work with NextAuth.js Session Tokens

See how NextAuth.js encrypts session tokens, why a signed-token verifier cannot decode them, and how to read them with the library’s helpers.

NextAuth.js · JWT · Authentication

Updated:

In the modern web development landscape, securing user authentication is paramount. Many developers turn to JSON Web Tokens (JWT) for their stateless, compact, and secure nature. When integrating authentication in a Next.js application, next-auth is a popular choice due to its simplicity and rich feature set.

However, a common stumbling block arises when developers try to use jwt.verify directly with tokens issued by next-auth. This article explores why this approach doesn’t work and how to handle JWTs correctly within a next-auth setup.

How NextAuth.js v4 Handles Session Tokens

With the JWT session strategy, NextAuth.js v4 encrypts session tokens by default. The token is a JWE, and v4 derives its encryption key from the configured secret using HKDF. A signed JWT is usually a JWS. jsonwebtoken.verify() verifies a JWS signature, so it cannot decrypt this session token.

Read the Token with the NextAuth.js Helper

For a Next.js Pages Router API route, use the helper from the same NextAuth.js version that issued the token. Set NEXTAUTH_SECRET in the server environment.

import { getToken } from "next-auth/jwt";

export default async function handler(req, res) {
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
  if (!token) {
    return res.status(401).json({ error: "Unauthorized" });
  }
  return res.status(200).json({ authenticated: true });
}

When you already have the raw token in a server-side script, use the library’s decoder:

import { decode } from "next-auth/jwt";

const token = process.env.NEXTAUTH_JWT;
const secret = process.env.NEXTAUTH_SECRET;
if (!token || !secret) throw new Error("Missing token or secret");

const payload = await decode({ token, secret });

These examples apply to the default JWT session strategy in v4. Match any custom decoder configured by the app. An OAuth provider’s access token is a separate credential and follows the provider’s validation rules.

Conclusion

Choose the decoder that matches the token’s issuer and format. For a NextAuth.js session token, use the library’s server-side helper so the application handles the token the same way it did when it issued it.

References

  1. NextAuth.js v4 JWT options
  2. NextAuth.js v4 getToken helper

Written by:

Full Stack Developer working with TypeScript, React and Node.js. Web development since 2019, across product interfaces, APIs and analytics.

A bit about me