fix: Recovery follow-ups from staging testing - #143
Merged
Merged
Conversation
yahyafakhroji
requested review from
gaghan430,
kevwilliams,
mattdjenkinson and
mdobush
September 25, 2026 15:56
…the passkey prompt Two failures found testing the flow end to end on staging. A user who leaves to fetch the code from their mail comes back after the two-minute token age limit. grecaptcha hands back a challenge aged from page load rather than from the execute() call, so the token verified with a 0.9 score and was still rejected as stale, and the route turned that into 'That code is invalid or has expired'. It blamed the code for a timing failure, on what is the normal path rather than an edge case. The code step now tolerates staleness alone; every other verdict still rejects, and the request step keeps its fresh-token gate. The sealed recovery ticket already gates this step. Recovery also left no passkey hint, so /login skipped its user-bound arm and nothing prompted until the user reloaded and signed in by hand. That arm fires exactly when there is a hint, no live session and the user has a passkey, which is the state recovery leaves behind, so the hint now names the recovered account. An earlier commit cleared it here by analogy with signup, where the account has no passkey and a half-built session to protect; neither holds after recovery.
…t paint The 'Can't use your passkey?' link rendered on the identifier screen whenever recovery was on and the org allowed passkeys, so every visitor saw it before anything had gone wrong. That is noise for the majority who sign in fine, and it advertises an account-recovery door to anyone loading the page, including someone probing addresses. It now appears on the passkey screen once an attempt has actually failed, which is the moment the user is looking for a way out, and it carries the loginName through so they do not retype it. The no_supported_method error page keeps its existing CTA, which is the dead end /login sends a methodless account to. resolveLoginView no longer takes accountRecoveryEnabled, and the loader stops sending it, since the identifier screen no longer decides anything with it.
yahyafakhroji
force-pushed
the
fix/recovery-followups
branch
from
September 25, 2026 16:02
efc0ba0 to
d148577
Compare
Contributor
🧪 Test Summary
|
yahyafakhroji
enabled auto-merge
September 25, 2026 16:03
Contributor
🧪 Test Summary
|
mattdjenkinson
approved these changes
Sep 25, 2026
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.
Problem
Three things found testing recovery end to end on staging.
A user who leaves to fetch the code from their mail comes back after the two-minute reCAPTCHA age limit. The token verified with a 0.9 score and was still rejected as stale, and the route turned that into "That code is invalid or has expired". It blamed the code for a timing failure, on what is the normal path rather than an edge case.
After recovery, no passkey prompt appeared until the user reloaded and signed in by hand.
armLoginPasskey's user-bound arm fires only when there is a hint, no live session and the user has a passkey, which is exactly the state recovery leaves behind, and recovery left no hint.The "Can't use your passkey?" link rendered on the identifier screen for every visitor, before anything had gone wrong.
Solution
The code step tolerates a stale token alone. Every other verdict still rejects, the request step keeps its fresh-token gate, and the sealed recovery ticket already gates the code step.
Recovery points the hint at the recovered account instead of clearing it. Signup clears it because that account has no passkey and a half-built session to protect; neither holds here.
The recovery link moves to the passkey screen, shown once an attempt fails, carrying the loginName through. The
no_supported_methoderror page keeps its existing CTA.Refs #112