Skip to content

fix(2fa): wire recovery code regeneration and make confirm dialog acc… - #983

Merged
thlpkee20-wq merged 2 commits into
RevoraOrg:masterfrom
Bigclavsfx:fix/2fa-regenerate-recovery-codes
Oct 22, 2026
Merged

thlpkee20-wq merged 2 commits into
RevoraOrg:masterfrom
Bigclavsfx:fix/2fa-regenerate-recovery-codes

Conversation

@Bigclavsfx

Copy link
Copy Markdown
Contributor

Closes #931
PR description

Title: fix(2fa): wire recovery code regeneration and make confirm dialog accessible

Problem

The setup flow warns that regenerating "will invalidate your current recovery codes", but confirmRegenerate only closed the dialog and the same codes stayed on screen.
The standalone backup codes page had a destructive-styled Regenerate button with an empty handler.
The confirmation overlay was a plain div with no dialog role, focus management or Escape handling.

Changes

Added an onRegenerateCodes?: () => Promise<string[]> prop to TwoFactorSetup and TwoFactorBackupCodesPage.
Confirming calls the handler and shows a pending state, with the buttons disabled while it runs.
On success the codes are replaced and the revealed/copied state is reset. In the setup wizard the acknowledgement checkbox is also cleared, so the user has to re-confirm they saved the new codes.
On failure an error is shown, the old codes are kept, and the dialog stays open so the user can retry or cancel.
The Regenerate button is hidden when no handler is supplied.
Added RegenerateCodesDialog, used by both surfaces:
role="dialog", aria-modal, a labelled heading, and a description.
Initial focus on Cancel.
Tab and Shift+Tab are trapped inside the dialog.
Escape closes it, except while the request is pending.
Focus returns to the trigger on close.
The dialog heading is now an h2 instead of h3, to satisfy axe's heading-order rule.

Tests (TwoFactorSetup.regenerate.test.tsx, 12 tests, both surfaces)

Regenerate calls the handler and renders the new codes, with reveal state reset.
The pending state disables the buttons.
A failure shows an error and keeps the old codes.
The button is hidden without a handler.
The dialog has its role, label, aria-modal and initial focus on Cancel.
Tab is trapped.
Escape closes the dialog and focus returns to the trigger.
jest-axe passes.

Out of scope

Enrolment secret sourcing.
The existing role="grid" markup on the codes grid.
The syntax error in the existing TwoFactorSetup.test.tsx.

…essible

Add an onRegenerateCodes(): Promise<string[]> prop to TwoFactorSetup and TwoFactorBackupCodesPage. Confirming now calls the handler, shows pending/error states, replaces the displayed codes and resets revealed/copied state. On failure the old codes are kept and an error is shown. The Regenerate action is hidden when no handler is supplied.

Extract RegenerateCodesDialog with role=dialog, aria-modal, a labelled heading, initial focus on Cancel, a Tab trap, Escape to close, and focus return to the trigger. Replace the no-op Regenerate button on the backup codes page.
@drips-wave

drips-wave Bot commented Oct 22, 2026

Copy link
Copy Markdown

@Bigclavsfx Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@thlpkee20-wq
thlpkee20-wq merged commit 68f561b into RevoraOrg:master Oct 22, 2026
1 of 5 checks passed
thlpkee20-wq added a commit that referenced this pull request Oct 22, 2026
…) passes

Reconcile code broken by "PR side wins" conflict resolution:
- App: merge the data-router migration (#978) with the full route table,
  AppLayout, footer and on-chain status; fix duplicated imports in App.test
- package.json: restore @stellar/freighter-api (#963), @stellar/stellar-sdk
  (#977) and qrcode/@types/qrcode (#967), which the lockfile already pinned
  or the PRs added, so the build resolves
- TwoFactorBackupCodesPage: merge initialCodes/codes props and copy-error
  handling (#972) with regeneration (#983)
- TwoFactorSetup/TwoFactorRecoveryFlow: fix dropped accountLabel prop and
  lockoutRemaining typo; NetworkSwitcher context: declare dismissal API (#982)
- KycSelfieCapture, RevenueCalendarCsvImport, reportExport, useContractRead,
  SaveAsDraft: restore merge-lost behaviour exercised by the PRs' tests
- Fix lint errors (missing react-hooks rule reference, useless escapes) and
  remove debug clipboard logging in OnChainStatusBadge

CI hygiene:
- typecheck runs on application code via tsconfig.app.json (tests run in vitest)
- fix pre-existing type errors (lucide icon types, Button size/danger props,
  missing type imports, vite env types, vitest/config defineConfig)
- repair broken/garbled tests; drop test cases for UI that no longer exists;
  replace long real-timer waits with fake timers so the suite runs quickly
- set per-file coverage thresholds that were never enforced to current levels

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

fix(2fa): 'Regenerate recovery codes' confirms success but never rotates codes, and its confirm dialog lacks dialog semantics

2 participants