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
breakView on GitHub (pinned to a1a16a5a77)
Solutions
- Set the AUTH_SECRET environment variable (or pass secret in the Auth config) so getToken can pick it up.
- Pass secret explicitly: getToken({ req, secret: process.env.AUTH_SECRET }).
- If you only need the raw token string, call getToken({ req, raw: true }) which does not require a secret.
- 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
- Always set AUTH_SECRET in every environment (dev, CI, prod).
- Pass secret explicitly to getToken in standalone route handlers and middleware.
- Use raw: true when you only need the token string, avoiding decode entirely.
- Add a startup check that fails fast when AUTH_SECRET is missing.
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
- Unsupported JWT Content Encryption Algorithm
- Hasura client error: Please provide an adminSecret
- Hasura client error: Please provide a graphql endpoint
- no matching decryption secret
- Must pass `req` to JWT getToken()
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/182eefaa087882c2.
Report an issue: GitHub.