nextauthjs/next-auth · error · AuthError

WebAuthn authenticator not found in database: ${JSON.stringi

Error message

WebAuthn authenticator not found in database: ${JSON.stringify({credentialID})}

What it means

After normalizing the credential ID, verifyAuthenticate looks up the authenticator in the database via adapter.getAuthenticator(). If no stored credential matches the credentialID from the assertion, this error is thrown because authentication cannot proceed against an unknown credential.

Source

Thrown at packages/core/src/lib/utils/webauthn-utils.ts:230

    request.body && typeof request.body.data === "string"
      ? (JSON.parse(request.body.data) as unknown)
      : undefined
  if (
    !data ||
    typeof data !== "object" ||
    !("id" in data) ||
    typeof data.id !== "string"
  ) {
    throw new AuthError("Invalid WebAuthn Authentication response")
  }

  // Reset the ID so we smooth out implementation differences
  const credentialID = toBase64(fromBase64(data.id))

  // Get authenticator from database
  const authenticator = await adapter.getAuthenticator(credentialID)
  if (!authenticator) {
    throw new AuthError(
      `WebAuthn authenticator not found in database: ${JSON.stringify({
        credentialID,
      })}`
    )
  }

  // Get challenge from request cookies
  const { challenge: expectedChallenge } = await webauthnChallenge.use(
    options,
    request.cookies,
    resCookies
  )

  // Verify the response
  let verification: VerifiedAuthenticationResponse
  try {
    const relayingParty = provider.getRelayingParty(options, request)
    verification = await provider.simpleWebAuthn.verifyAuthenticationResponse({

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Confirm the authenticator exists via adapter.getAuthenticator(credentialID) with the exact normalized base64 ID
  2. Check that your adapter's getAuthenticator compares/stores IDs in the same encoding used here
  3. Point the app at the same database where registration stored the credential
  4. Re-register the passkey if the server-side record was deleted

Example fix

// before
const auth = await adapter.getAuthenticator(rawId) // wrong encoding
// after
const auth = await adapter.getAuthenticator(toBase64(fromBase64(rawId)))
Defensive patterns

Strategy: validation

Validate before calling

const credentialID = toBase64(fromBase64(data.id))
const authenticator = await adapter.getAuthenticator(credentialID)
if (!authenticator) {
  // prompt re-registration instead of calling verifyAuthenticate
}

Try / catch

try {
  await verifyAuthenticate(data)
} catch (e) {
  if (e instanceof AuthError && e.message.includes('authenticator not found')) {
    // offer the user to register a new passkey
  }
}

Prevention

When it happens

Trigger: A passkey assertion is presented whose credentialID does not exist in the adapter's authenticator table — e.g. credential deleted, different database/environment, or ID encoding mismatch.

Common situations: Switching databases or environments between registration and login; user deleted the passkey server-side but the browser still offers it; custom adapters with getAuthenticator not implemented or comparing raw vs base64-encoded IDs.

Understand the failure class

Related errors


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