{"record":{"id":"c8dc8bb994ebd710","repo":"QuantumNous/new-api","slug":"unable-to-build-passkey-assertion","errorCode":null,"errorMessage":"Unable to build Passkey assertion","messagePattern":"Unable to build Passkey assertion","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/features/auth/secure-verification/api.ts","lineNumber":162,"sourceCode":"    const publicKey = prepareCredentialRequestOptions(\n      beginResponse.data?.options ?? beginResponse.data\n    )\n    const flowToken = beginResponse.data?.flow_token\n    if (!flowToken) {\n      throw new Error(i18next.t('Verification flow expired'))\n    }\n\n    const credential = (await navigator.credentials.get({\n      publicKey,\n    })) as PublicKeyCredential | null\n\n    if (!credential) {\n      throw new Error(i18next.t('Passkey verification was cancelled'))\n    }\n\n    const assertion = buildAssertionResult(credential)\n    if (!assertion) {\n      throw new Error(i18next.t('Unable to build Passkey assertion'))\n    }\n\n    const finishResponse = await finishPasskeyVerification(flowToken, assertion)\n    if (!finishResponse.success) {\n      throw new Error(\n        finishResponse.message || i18next.t('Passkey verification failed')\n      )\n    }\n\n    if (!finishResponse.data?.proof_token) {\n      throw new Error(i18next.t('Verification proof was not returned'))\n    }\n    return finishResponse.data\n  } catch (error: unknown) {\n    if (error instanceof DOMException && error.name === 'NotAllowedError') {\n      throw new Error(\n        i18next.t('Passkey verification was cancelled or timed out'),\n        { cause: error }","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/auth/secure-verification/api.ts#L144-L180","documentation":"Thrown in verifyPasskey() when buildAssertionResult(credential) returns a falsy value after a successful navigator.credentials.get(). The helper converts the PublicKeyCredential into the JSON-serializable assertion the finish endpoint expects; null means the credential's response was missing required fields (clientDataJSON, authenticatorData, signature) or had an unexpected shape.","triggerScenarios":"The authenticator returns a credential whose response lacks signature or authenticatorData; the browser hands back a credential object whose response is an unexpected subtype; a polyfill or testing library returning a minimal fake credential.","commonSituations":"Testing with mocked navigator.credentials that omits response fields; exotic authenticators or security keys with non-standard attestation responses; browser extension interfering with the credentials API.","solutions":["Log the credential object (id, type, response keys) to see which field is absent.","If using a mock in tests, make it include response.clientDataJSON, authenticatorData, signature (and userHandle when present), base64url-encoded.","Try a different authenticator/device to rule out the specific security key.","Harden buildAssertionResult to report which field was missing instead of returning null."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"const isUsableAssertion = (a: unknown): boolean =>\n  typeof a === 'object' && a !== null &&\n  typeof (a as { id?: unknown }).id === 'string' &&\n  typeof (a as { response?: { signature?: unknown } }).response?.signature === 'string'","tryCatchPattern":"try {\n  await verify('passkey', scope)\n} catch (e) {\n  if (/assertion/i.test(getErrorMessage(e))) {\n    promptUserToRetryWithDifferentAuthenticator()\n  }\n  throw e\n}","preventionTips":["When mocking navigator.credentials in tests, include clientDataJSON, authenticatorData and signature","Harden buildAssertionResult to name the missing field instead of returning null","Retry with another device/security key when the authenticator emits malformed responses"],"tags":["passkey","webauthn","assertion","data-shape"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}