FuzeFront
Default · recovery-code entry (reached from “Recovery code” in frame 05, only when recoveryAvailable: true)

Use a recovery code

Enter one of the recovery codes you saved when you turned on two-factor authentication for amara@rowan.co.

Each code works once. Spaces and dashes are fine — type it the way you saved it.

One text field, not OTP cells: the code is opaque (up to 64 characters; today's look like XXXX-XXXX-XXXX-XXXX) and the client must not check its format. Disable “Verify” only while the trimmed value is empty. Send exactly what was typed as recoveryCode; the server strips spaces and dashes and ignores case. No numeric keyboard, no auto-capitalise, no spellcheck.

State · verifying (in flight, no double submit)

Use a recovery code

Enter one of the recovery codes you saved when you turned on two-factor authentication for amara@rowan.co.

Each code works once. Spaces and dashes are fine — type it the way you saved it.
State · code didn't work (401 RECOVERY_CODE_INVALID, fail-closed, retry allowed)

Use a recovery code

That didn't work. Enter one of your saved recovery codes.

That recovery code didn't work. Check it for typos, or try another of your codes — each code works only once.

One message for wrong, already used and replaced codes — the API makes them look the same on purpose, so the UI never says which. Nothing about time (“codes change every 30 seconds” is authenticator-app copy and is wrong here). The challenge is still live: keep the typed value, focus and select it, and allow another try.

State · too many attempts (429 RATE_LIMITED, fail-closed)

Use a recovery code

Each code works once. Spaces and dashes are fine — type it the way you saved it.

Same limiter, same copy as frame 05. Wait comes from the 429's Retry-After header (seconds): show a live m:ss countdown in the callout and on the disabled button, and enable “Try again” when it reaches zero. If the header is missing or not a positive integer, show “Wait a few minutes, then try again.” with the button enabled. The entered code is kept and not marked red (it wasn't judged wrong). A retry that still gets 429 restarts the countdown from the new header.

State · sign-in expired (401 NOT_ACTIVE — challenge unknown/expired/completed)

No promise about the recovery code here. NOT_ACTIVE also covers an already-completed challenge — e.g. a successful verify whose reply was lost, then retried — in which case the code was consumed. The old challengeId is dead: no field and no “Try again” on this screen.

State · couldn't check the code (network / 5xx / unrecognised error)

Use a recovery code

Each code works once. Spaces and dashes are fine — type it the way you saved it.

This copy does not say the code is still unused: if the server accepted it but the reply was lost, the code is gone. Never describe this as a wrong code.

Frame 07 · Recovery-code entry (step-up, from frame 05) · /login/mfa ← Step-up Back to all frames
Review · mfa-management approve or reject each flow ↓
mfa-overviewapproved · izzywdev
totp-enrollapproved · izzywdev
sms-enrollapproved · izzywdev
recovery-codesapproved · izzywdev
mfa-step-upapproved · izzywdev