{"record":{"id":"1a9968a887857a77","repo":"nextauthjs/next-auth","slug":"webauthn-authenticator-not-found-in-database-js","errorCode":null,"errorMessage":"WebAuthn authenticator not found in database: ${JSON.stringify({credentialID})}","messagePattern":"WebAuthn authenticator not found in database: (.+?)\\)\\}","errorType":"exception","errorClass":"AuthError","httpStatus":null,"severity":"error","filePath":"packages/core/src/lib/utils/webauthn-utils.ts","lineNumber":230,"sourceCode":"    request.body && typeof request.body.data === \"string\"\n      ? (JSON.parse(request.body.data) as unknown)\n      : undefined\n  if (\n    !data ||\n    typeof data !== \"object\" ||\n    !(\"id\" in data) ||\n    typeof data.id !== \"string\"\n  ) {\n    throw new AuthError(\"Invalid WebAuthn Authentication response\")\n  }\n\n  // Reset the ID so we smooth out implementation differences\n  const credentialID = toBase64(fromBase64(data.id))\n\n  // Get authenticator from database\n  const authenticator = await adapter.getAuthenticator(credentialID)\n  if (!authenticator) {\n    throw new AuthError(\n      `WebAuthn authenticator not found in database: ${JSON.stringify({\n        credentialID,\n      })}`\n    )\n  }\n\n  // Get challenge from request cookies\n  const { challenge: expectedChallenge } = await webauthnChallenge.use(\n    options,\n    request.cookies,\n    resCookies\n  )\n\n  // Verify the response\n  let verification: VerifiedAuthenticationResponse\n  try {\n    const relayingParty = provider.getRelayingParty(options, request)\n    verification = await provider.simpleWebAuthn.verifyAuthenticationResponse({","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/core/src/lib/utils/webauthn-utils.ts#L212-L248","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the authenticator exists via adapter.getAuthenticator(credentialID) with the exact normalized base64 ID","Check that your adapter's getAuthenticator compares/stores IDs in the same encoding used here","Point the app at the same database where registration stored the credential","Re-register the passkey if the server-side record was deleted"],"exampleFix":"// before\nconst auth = await adapter.getAuthenticator(rawId) // wrong encoding\n// after\nconst auth = await adapter.getAuthenticator(toBase64(fromBase64(rawId)))","handlingStrategy":"validation","validationCode":"const credentialID = toBase64(fromBase64(data.id))\nconst authenticator = await adapter.getAuthenticator(credentialID)\nif (!authenticator) {\n  // prompt re-registration instead of calling verifyAuthenticate\n}","typeGuard":null,"tryCatchPattern":"try {\n  await verifyAuthenticate(data)\n} catch (e) {\n  if (e instanceof AuthError && e.message.includes('authenticator not found')) {\n    // offer the user to register a new passkey\n  }\n}","preventionTips":["Use one consistent database across environments","Store and compare credential IDs in the same base64 encoding","Clean up browser credentials when deleting them server-side","Add integration tests covering register-then-login round trips"],"tags":["webauthn","database","adapter","credential-not-found"],"backgroundTag":"credential-not-found","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}