Skip to content

fix: Recovery follow-ups from staging testing - #143

Merged
yahyafakhroji merged 3 commits into
mainfrom
fix/recovery-followups
Sep 25, 2026
Merged

yahyafakhroji merged 3 commits into
mainfrom
fix/recovery-followups

Conversation

@yahyafakhroji

Copy link
Copy Markdown
Collaborator

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_method error page keeps its existing CTA.

Refs #112

…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.
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Summary

Job Status
E2E (fast, fake provider) ✅ success
Unit Tests (component) (0) ✅ success
Unit Tests (component) (1) ✅ success
Unit Tests (component) (2) ✅ success

View workflow run

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Summary

Job Status
E2E (fast, fake provider) ✅ success
Unit Tests (component) (0) ✅ success
Unit Tests (component) (1) ✅ success
Unit Tests (component) (2) ✅ success

View workflow run

@yahyafakhroji
yahyafakhroji merged commit 5fb8b0d into main Sep 25, 2026
16 checks passed
@yahyafakhroji
yahyafakhroji deleted the fix/recovery-followups branch September 25, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants