fix(passkeys): handle authenticator user-verification failures#20821
Draft
vpomerleau wants to merge 1 commit into
Draft
fix(passkeys): handle authenticator user-verification failures#20821vpomerleau wants to merge 1 commit into
vpomerleau wants to merge 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Improves passkey UX and operational noise by classifying WebAuthn “user verification not performed” failures (UV=0) as an expected, user-actionable error, returning a specific client-visible errno (233) for registration, and surfacing clearer guidance in the Settings UI when registration stalls.
Changes:
- Server: Introduces
UserVerificationRequiredErrorin the passkey adapter and maps registration UV failures toAppError.passkeyUserVerificationRequired()(HTTP 422 / errno 233), while tagging metrics and avoiding Sentry capture for this expected case. - Client: Registers
errno 233for localized display and adds a timed, non-blocking “Taking a while?” Banner under the passkey creation spinner (plus tests/stories). - Tests: Adds adapter/service unit coverage for UV classification + client component coverage for the delayed hint and errno-233 UX.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/fxa-settings/src/lib/auth-errors/en.ftl | Adds localized string for auth-error-233. |
| packages/fxa-settings/src/lib/auth-errors/auth-errors.ts | Registers PASSKEY_USER_VERIFICATION_REQUIRED mapping to errno 233 for UI. |
| packages/fxa-settings/src/lib/auth-errors/auth-errors.test.ts | Verifies getErrorFtlId resolves auth-error-233. |
| packages/fxa-settings/src/components/Settings/PagePasskeyAdd/index.tsx | Adds delayed informational Banner when passkey ceremony appears stalled. |
| packages/fxa-settings/src/components/Settings/PagePasskeyAdd/index.test.tsx | Tests delayed hint behavior and errno-233 handling without Sentry capture. |
| packages/fxa-settings/src/components/Settings/PagePasskeyAdd/index.stories.tsx | Adds Storybook scenario to showcase stalled ceremony hint (0ms delay). |
| packages/fxa-settings/src/components/Settings/PagePasskeyAdd/en.ftl | Adds localized copy for the stalled-ceremony hint banner. |
| libs/accounts/passkey/src/lib/webauthn-adapter.ts | Classifies SimpleWebAuthn UV failures into a typed UserVerificationRequiredError. |
| libs/accounts/passkey/src/lib/webauthn-adapter.spec.ts | Ensures UV failures are classified and non-UV failures are not misclassified. |
| libs/accounts/passkey/src/lib/passkey.service.ts | Maps registration UV failures to 422/233 (no Sentry), tags auth failures in metrics. |
| libs/accounts/passkey/src/lib/passkey.service.spec.ts | Adds coverage for Sentry capture suppression + metric tagging on UV failure. |
| libs/accounts/errors/src/index.spec.ts | Adds test for AppError.passkeyUserVerificationRequired(). |
| libs/accounts/errors/src/constants.ts | Adds ERRNO.PASSKEY_USER_VERIFICATION_REQUIRED = 233. |
| libs/accounts/errors/src/app-error.ts | Adds AppError.passkeyUserVerificationRequired() (422 / errno 233). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Because: * Passkey registration threw a 500 and was captured in Sentry (FXA-AUTH-39Z) whenever an authenticator completed the ceremony without user verification (UV=0) — e.g. a roaming security key with no PIN — while the server requires UV for the AAL2 invariant. This is expected client/authenticator behaviour, not a server fault. * Users saw a generic "Passkey registration failed" with no guidance, and a stalled device prompt showed only a spinner. This commit: * Classifies the UV failure in the passkey adapter (UserVerificationRequiredError) and returns a 422 (errno PASSKEY_USER_VERIFICATION_REQUIRED = 233) instead of a 500, without capturing it in Sentry; keeps a passkey.registration.failed{reason=userVerificationFailed} metric for sizing. * Applies the same classification to the authentication path (metric only; keeps the existing 401, no distinct errno to avoid leaking credential capability). * Registers errno 233 on the client with a device-agnostic message guiding users to add a screen lock, PIN, or biometric, or choose another method. * Adds a non-blocking, device-agnostic info Banner (role=status/aria-live) under the "Creating passkey…" spinner once the ceremony stalls, plus a Storybook case. * Keeps user verification strictly required; adds unit and component tests. closes FXA-14080
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Because
FXA-AUTH-39Z): when an authenticator completes the ceremony without performing user verification (UV=0) — e.g. a roaming security key with no PIN — SimpleWebAuthn rejects it because the server requires UV for the AAL2 invariant. This is expected client/authenticator behaviour, but it surfaced as an HTTP 500 and was logged as Sentry noise.This pull request
libs/accounts/passkey/src/lib/webauthn-adapter.tsvia a typedUserVerificationRequiredError, so the passkey service returns a 422 (errno 233,PASSKEY_USER_VERIFICATION_REQUIRED) instead of a 500 and skips Sentry capture for this expected case, while keeping apasskey.registration.failed{reason=userVerificationFailed}metric.libs/accounts/passkey/src/lib/passkey.service.ts(metric only; keeps the existing 401 with no distinct errno, to avoid leaking credential capability on sign-in).errno 233tolibs/accounts/errors/src/{constants.ts,app-error.ts}and registers it on the client (packages/fxa-settings/src/lib/auth-errors/) with a device-agnostic message guiding users to add a screen lock, PIN, or biometric, or choose another method.Banner(role="status"/aria-live) under the "Creating passkey…" spinner once the ceremony stalls, inpackages/fxa-settings/src/components/Settings/PagePasskeyAdd/index.tsx, plus a Storybook case.Issue that this pull request solves
Closes: FXA-14080
Checklist
Put an
xin the boxes that applyHow to review (Optional)
webauthn-adapter.ts(theisUserVerificationErrorclassification + both verify paths),passkey.service.ts(catch-block branching), andPagePasskeyAdd/index.tsx(the timed hint Banner).constants.ts→app-error.ts) → passkey adapter → passkey service → client error registration (auth-errors.ts+en.ftl) →PagePasskeyAdd.Screenshots (Optional)
Stalled-ceremony info banner ("Taking a while? …")

Other information (Optional)
errno 233/ HTTP 422 (PASSKEY_USER_VERIFICATION_REQUIRED).