nextauthjs/next-auth · error · MissingSecret

Must pass `secret` if not set to JWT getToken()

Error message

Must pass `secret` if not set to JWT getToken()

What it means

Auth.js's getToken() decrypts/verifies the session JWT, which requires the same secret used to encode it. If no secret is available (not passed as an argument and not configured in the Auth config), it throws MissingSecret instead of guessing. This is thrown in packages/core/src/jwt.ts when getToken() is called with raw=false and no secret.

Source

Thrown at packages/core/src/jwt.ts:191

  const authorizationHeader = headers.get("authorization")

  if (!token && authorizationHeader?.split(" ")[0] === "Bearer") {
    const urlEncodedToken = authorizationHeader.split(" ")[1]
    try {
      token = decodeURIComponent(urlEncodedToken)
    } catch {
      // Malformed percent-encoding makes the Bearer token invalid
      return null
    }
  }

  if (!token) return null

  if (raw) return token

  if (!secret)
    throw new MissingSecret("Must pass `secret` if not set to JWT getToken()")

  try {
    return await _decode({ token, secret, salt })
  } catch {
    return null
  }
}

async function getDerivedEncryptionKey(
  enc: string,
  keyMaterial: Parameters<typeof hkdf>[1],
  salt: Parameters<typeof hkdf>[2]
) {
  let length: number
  switch (enc) {
    case "A256CBC-HS512":
      length = 64
      break

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Set the AUTH_SECRET environment variable (or pass secret in the Auth config) so getToken can pick it up.
  2. Pass secret explicitly: getToken({ req, secret: process.env.AUTH_SECRET }).
  3. If you only need the raw token string, call getToken({ req, raw: true }) which does not require a secret.
  4. Verify the runtime actually loads .env files (e.g. next dev loads .env.local, but plain Node needs dotenv).

Example fix

// before
const token = await getToken({ req })
// after
const token = await getToken({ req, secret: process.env.AUTH_SECRET })
Defensive patterns

Strategy: validation

Validate before calling

if (!process.env.AUTH_SECRET) throw new Error('AUTH_SECRET must be set before calling getToken()')
const token = await getToken({ req, secret: process.env.AUTH_SECRET })

Type guard

function hasSecret(opts: { secret?: string }): opts is { secret: string } {
  return typeof opts.secret === 'string' && opts.secret.length > 0
}

Try / catch

try {
  const token = await getToken({ req })
} catch (e) {
  if (e instanceof MissingSecret) {
    // fall back to unauthenticated response or configure secret
  }
}

Prevention

When it happens

Trigger: Calling getToken({ req, raw: false }) (the default) without a secret argument while the Auth() config also has no secret set. Passing raw: true bypasses this because the raw token is returned without decoding.

Common situations: Reading the session in a route handler/middleware in a separate entry point that does not pass the AUTH_SECRET; deploying without the AUTH_SECRET environment variable; calling getToken outside the framework where options.secret was never propagated.

Related errors


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/182eefaa087882c2. Report an issue: GitHub.