nextauthjs/next-auth · error · MissingCSRF

CSRF token was missing during an action ${action}

Error message

CSRF token was missing during an action ${action}

What it means

Auth.js throws MissingCSRF when a state-changing auth action (signin, signout, callback) arrives without the CSRF token cookie that was set when the session/page was rendered. The library requires the csrfToken cookie value to match the csrfToken POSTed in the form body to prevent cross-site request forgery against the auth endpoints.

Source

Thrown at packages/core/src/lib/actions/callback/oauth/csrf-token.ts:59

      // If this is a POST request and the CSRF Token in the POST request matches
      // the cookie we have already verified is the one we have set, then the token is verified!
      const csrfTokenVerified = isPost && csrfToken === bodyValue

      return { csrfTokenVerified, csrfToken }
    }
  }

  // New CSRF token
  const csrfToken = randomString(32)
  const csrfTokenHash = await createHash(`${csrfToken}${options.secret}`)
  const cookie = `${csrfToken}|${csrfTokenHash}`

  return { cookie, csrfToken }
}

export function validateCSRF(action: AuthAction, verified?: boolean) {
  if (verified) return
  throw new MissingCSRF(`CSRF token was missing during an action ${action}`)
}

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Fetch GET /api/auth/csrf first, then POST the csrfToken value in the request body while sending cookies (credentials: 'include' in browsers)
  2. Verify cookies are enabled and not blocked: check Secure/SameSite attributes match HTTPS deployment and that the request is same-site, or configure cookies.cookies.sessionToken/partner options in AuthOptions
  3. If using a custom login form, include the csrfToken as a hidden field (NextAuth useSession/csrf helpers) in the POSTed form data
  4. Check reverse proxy/CDN configuration (e.g. Cloudflare, Vercel rewrites) is not stripping the Set-Cookie header that delivers the CSRF cookie
  5. Clear stale cookies and retry: an expired/rotated CSRF cookie can mismatch after secret changes; ensure NEXTAUTH_SECRET/AUTH_SECRET is stable across instances

Example fix

// before
csrfToken: "useSecureCookies" ? undefined : null,

// after
const res = await fetch(`${baseUrl}/api/auth/csrf`, { credentials: 'include' });
const { csrfToken } = await res.json();
await fetch(`${baseUrl}/api/auth/signin/credentials`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  credentials: 'include',
  body: new URLSearchParams({ csrfToken, username, password }),
});
Defensive patterns

Strategy: try-catch

Validate before calling

const r = await fetch(`${baseUrl}/api/auth/csrf`, { credentials: 'include' });
const { csrfToken } = await r.json();
if (!csrfToken) throw new Error('No CSRF token available');

Type guard

function hasCsrf(x: unknown): x is { csrfToken: string } {
  return typeof x === 'object' && x !== null && typeof (x as any).csrfToken === 'string' && (x as any).csrfToken.length > 0;
}

Try / catch

try {
  await signIn(provider, options, formData);
} catch (e) {
  if (e instanceof MissingCSRF || /CSRF token was missing/.test(String(e))) {
    // refresh CSRF token via GET /api/auth/csrf and retry once with credentials: 'include'
  }
}

Prevention

When it happens

Trigger: POSTing to /api/auth/callback/*, /api/auth/signout, or /api/auth/signin/* without the authjs.csrf-token (next-auth.csrf-token) cookie, or with a body/form value that does not include the csrfToken returned by GET /api/auth/csrf.

Common situations: Calling auth endpoints with fetch/axios or curl without first fetching /api/auth/csrf and sending credentials: 'include'; browser blocking cookies (SameSite/Secure/HTTPS mismatch, cross-subdomain requests); custom sign-in pages that forget the hidden csrfToken input; server-side redirects that drop cookies; reverse proxies stripping Set-Cookie headers.

Related errors


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