nextauthjs/next-auth · error · InvalidCheck

WebAuthn challenge was missing

Error message

WebAuthn challenge was missing

What it means

The WebAuthn check's `use` throws InvalidCheck("WebAuthn challenge was missing") when decoding the webauthnChallenge cookie at the callback/verification step yields no payload. The challenge is stored in a cookie when the WebAuthn flow starts and must be present to verify the authenticator's response.

Source

Thrown at packages/core/src/lib/actions/callback/oauth/checks.ts:270

  async use(
    options: InternalOptions<WebAuthnProviderType>,
    cookies: RequestInternal["cookies"],
    resCookies: Cookie[]
  ): Promise<WebAuthnChallengePayload> {
    const cookieValue = cookies?.[options.cookies.webauthnChallenge.name]

    const parsed = await parseCookie("webauthnChallenge", cookieValue, options)

    const payload = await decode<WebAuthnChallengePayload>({
      secret: options.jwt.secret,
      token: parsed,
      salt: webauthnChallengeSalt,
    })

    // Clear the WebAuthn challenge cookie after use
    clearCookie("webauthnChallenge", options, resCookies)

    if (!payload) throw new InvalidCheck("WebAuthn challenge was missing")

    return payload
  },
}

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Ensure the client sends credentials with every Auth.js request (fetch with credentials: "include" / axios withCredentials).
  2. Verify cookies survive in the target browser; test outside private mode and check SameSite/Secure settings relative to AUTH_URL.
  3. Make sure the WebAuthn challenge and verification hit the same origin/host configured in AUTH_URL.
  4. If building a native client, implement a cookie jar that persists the webauthnChallenge cookie across the two requests.
  5. Restart the WebAuthn flow to obtain a fresh challenge.

Example fix

// before: fetch drops cookies
await fetch("/api/auth/callback/webauthn", { method: "POST", body })
// after
await fetch("/api/auth/callback/webauthn", {
  method: "POST",
  body,
  credentials: "include",
})
Defensive patterns

Strategy: validation

Validate before calling

// client-side: ensure cookies are sent with every auth request
const res = await fetch("/api/auth/session", { credentials: "include" })
if (!res.headers.get("set-cookie") && !document.cookie.includes("webauthnChallenge")) {
  console.warn("Challenge cookie may not persist; check SameSite/credentials")
}

Try / catch

try {
  await signIn("webauthn", { challenge })
} catch (e) {
  if (e?.message?.includes("WebAuthn challenge was missing")) {
    // restart the WebAuthn flow to get a fresh challenge cookie
  }
}

Prevention

When it happens

Trigger: During WebAuthn sign-in or registration verification, the webauthnChallenge cookie is absent or undecodable (decode returns falsy payload) when use() runs after clearing the cookie. Commonly caused by the browser not storing/sending the cookie between the challenge request and the authenticate response.

Common situations: Cross-site (Safari/ITP) or third-party cookie blocking dropping the challenge cookie; API clients (mobile apps, fetch without credentials: 'include') that don't persist cookies; AUTH_URL/origin mismatch invalidating Secure cookies; challenge issued on a different domain than verification.

Related errors


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