Skip to content

feat(identity): recovery codes status + download/print (closes #180)#203

Merged
antosubash merged 8 commits into
mainfrom
issue-180-recovery-codes
May 20, 2026
Merged

feat(identity): recovery codes status + download/print (closes #180)#203
antosubash merged 8 commits into
mainfrom
issue-180-recovery-codes

Conversation

@antosubash
Copy link
Copy Markdown
Owner

Summary

Closes #180.

  • 2FA management page now shows a neutral status row "Recovery codes: N remaining" when recoveryCodesLeft >= 4. The existing alerts at <=3, =1, and =0 (warning / danger styles) are kept — those already handled the low-stock case.
  • ShowRecoveryCodes page gains Download (.txt) and Print buttons:
    • Download writes a header line (SimpleModule recovery codes — generated for <email> on <date>) followed by the 10 codes, one per line, named simplemodule-recovery-codes.txt.
    • Print uses an inline @media print stylesheet that hides everything outside the codes block and re-styles the codes as black-on-white monospace.
  • Both render paths in AccountSecurityEndpoint (initial 2FA enable that auto-generates codes, and explicit "Reset recovery codes") now thread userEmail + generatedAt so the header is accurate.
  • Added a comment on GenerateRecoveryCodesEndpoint explaining that codes are hashed (like passwords) and the only honest paths are download/print at generation time or regenerate (invalidates prior set). Prevents future contributors from inventing a retrieve-existing-codes endpoint that can't actually exist.

Test plan

  • dotnet build — green
  • dotnet test modules/Users/tests — 70/70
  • Manual smoke: enable 2FA → see codes screen → Download writes a file with the header + codes; Print shows codes only (no nav/buttons). Reset recovery codes from the 2FA page → same behavior. With 7 codes remaining, the 2FA page shows "Recovery codes: 7 remaining" as a plain text-text-muted line; redeem until at 3 remaining and the existing warning takes over.

Not in scope

  • A separate translation file beyond en.json. The codebase only ships English today; other languages will pick up the new keys when their files exist.
  • Recovery-code redemption tests — covered by the existing LoginWithRecoveryCodeEndpoint tests; CountRecoveryCodesAsync is the Identity primitive and is not re-wrapped here.

- 2FA management page now shows 'Recovery codes: N remaining' as a
  neutral status row when count is >= 4. The existing low-threshold
  alerts at <=3, 1, and 0 are kept.
- ShowRecoveryCodes page gains Download (.txt) and Print buttons.
  Download writes a header line (user email + generated-at) followed
  by the codes.
- Print uses an inline @media print stylesheet that hides chrome and
  re-styles the codes block as black-on-white monospace.
- Both render paths in AccountSecurityEndpoint now thread userEmail
  and generatedAt through so the header is accurate.
- Added a comment on GenerateRecoveryCodesEndpoint explaining that
  codes are hashed (like passwords) and the only honest paths are
  'download/print at generation time' or 'regenerate (invalidates
  prior set)'. Prevents future contributors from trying to add a
  retrieve-existing-codes endpoint.

No new API contracts; UI-only + props passthrough. Existing Users
suite still 70/70.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 15, 2026

Deploying simplemodule-website with  Cloudflare Pages  Cloudflare Pages

Latest commit: c9b24de
Status: ✅  Deploy successful!
Preview URL: https://a4cb2b22.simplemodule-website.pages.dev
Branch Preview URL: https://issue-180-recovery-codes.simplemodule-website.pages.dev

View logs

Wrap long downloadCodes signature to satisfy 100-char line width.
- Pass statusKey instead of hardcoded English message; resolve via i18n on client (one was a duplicate of TwoFactor.Status.AuthenticatorVerified).
- Use user.Email directly and DateTimeOffset.ToString("O") to match module convention.
- Make userEmail/generatedAt required props; drop dead-code fallbacks.
- Trim verbose comment in GenerateRecoveryCodesEndpoint to the load-bearing why.
@antosubash antosubash enabled auto-merge (squash) May 20, 2026 19:03
@antosubash antosubash merged commit 928750f into main May 20, 2026
6 checks passed
@antosubash antosubash deleted the issue-180-recovery-codes branch May 20, 2026 19:07
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.

Identity: recovery codes status &amp; download

1 participant