QuantumNous/new-api · error · Error

Unable to build Passkey assertion

Error message

Unable to build Passkey assertion

What it means

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.

Source

Thrown at web/src/features/auth/secure-verification/api.ts:162

    const publicKey = prepareCredentialRequestOptions(
      beginResponse.data?.options ?? beginResponse.data
    )
    const flowToken = beginResponse.data?.flow_token
    if (!flowToken) {
      throw new Error(i18next.t('Verification flow expired'))
    }

    const credential = (await navigator.credentials.get({
      publicKey,
    })) as PublicKeyCredential | null

    if (!credential) {
      throw new Error(i18next.t('Passkey verification was cancelled'))
    }

    const assertion = buildAssertionResult(credential)
    if (!assertion) {
      throw new Error(i18next.t('Unable to build Passkey assertion'))
    }

    const finishResponse = await finishPasskeyVerification(flowToken, assertion)
    if (!finishResponse.success) {
      throw new Error(
        finishResponse.message || i18next.t('Passkey verification failed')
      )
    }

    if (!finishResponse.data?.proof_token) {
      throw new Error(i18next.t('Verification proof was not returned'))
    }
    return finishResponse.data
  } catch (error: unknown) {
    if (error instanceof DOMException && error.name === 'NotAllowedError') {
      throw new Error(
        i18next.t('Passkey verification was cancelled or timed out'),
        { cause: error }

View on GitHub (pinned to e2c7aa7b10)

Solutions

  1. Log the credential object (id, type, response keys) to see which field is absent.
  2. If using a mock in tests, make it include response.clientDataJSON, authenticatorData, signature (and userHandle when present), base64url-encoded.
  3. Try a different authenticator/device to rule out the specific security key.
  4. Harden buildAssertionResult to report which field was missing instead of returning null.
Defensive patterns

Strategy: type-guard

Type guard

const isUsableAssertion = (a: unknown): boolean =>
  typeof a === 'object' && a !== null &&
  typeof (a as { id?: unknown }).id === 'string' &&
  typeof (a as { response?: { signature?: unknown } }).response?.signature === 'string'

Try / catch

try {
  await verify('passkey', scope)
} catch (e) {
  if (/assertion/i.test(getErrorMessage(e))) {
    promptUserToRetryWithDifferentAuthenticator()
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of QuantumNous/new-api@e2c7aa7b10 (2026-08-15). Data as JSON: /api/errors/c8dc8bb994ebd710. Report an issue: GitHub.