fix(mfa): route passkey-verify errors through resolveAuthStep#70
Merged
Conversation
onVerifyWithPasskey returned early on any errors.length before ever calling resolveAuthStep, so its 'locked' branch (setWebauthnLocked, which renders AuthorizerMfaLocked) was dead code - a locked-account error surfaced as a generic dismissable message instead of the lockout screen, unlike every other login-capable component that already routes through resolveAuthStep unconditionally. Keep the isUserDismissed (NotAllowedError/AbortError) short-circuit, since those are ceremony cancellations that should stay silent, not be shown as an error or misread as a lock. Adds e2e coverage for the interaction this enables: a passkey-verify lockout now correctly aborts a pending WebOTP navigator.credentials .get() listener (webauthnLocked in AuthorizerVerifyOtp's effect deps). WebOTP is Android-Chrome-only, so the test shims window.OTPCredential and the 'otp' branch of navigator.credentials.get to force the effect's feature-detection true - it proves the abort wiring, not real WebOTP interop. Closes #69
size-limit report 📦
|
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.
Summary
onVerifyWithPasskey(AuthorizerVerifyOtp.tsx) returned early on anyerrors.lengthbefore ever callingresolveAuthStep, making its'locked'branch (setWebauthnLocked→ rendersAuthorizerMfaLocked) dead code. A locked-account error during passkey verify surfaced as a generic dismissable message instead of the dedicated lockout screen — unlike every other login-capable component, which already routes unconditionally throughresolveAuthStep.isUserDismissed(NotAllowedError/AbortError) short-circuit for cancelled ceremonies, but everything else now goes throughresolveAuthStepso the locked path is reachable.navigator.credentials.get()listener (webauthnLockedis inAuthorizerVerifyOtp's WebOTP effect's dependency array specifically for this). WebOTP is Android-Chrome-only, so the new test shimswindow.OTPCredentialand theotp-branch ofnavigator.credentials.getto force the effect's feature-detection true — it proves the abort wiring, not real WebOTP interop, and says so in its own comments.Closes #69
Test plan
npm run test(tsc --noEmit): clean.example/e2e/Playwright suite (local backend + app, Chromium): 7/7 passing, including the 6 pre-existing MFA specs (no regression) and the newmfa-webotp-abort.spec.ts.onVerifyWithPasskeychange locally and re-ran — the new test fails (times out waiting for the abort signal) as expected, confirming it isn't a vacuous pass.