Skip to content

Conversation

@sricharan-varanasi
Copy link
Contributor

@sricharan-varanasi sricharan-varanasi commented Jan 2, 2026

  • Tests for the changes have been added
  • Related documentation has been added / updated
  • OSS packages added to Curious open source credit page
  • Delivered the fix or feature branches into develop or release branches via Squash and Merge (to keep clean history)

📝 Description

🔗 Jira Ticket M2-10278

This PR refactors the MFA disable flow from a 2-step process to a more secure 3-step process, and updates all associated tests to match the new implementation.

Previous 2-step flow:

  1. Initiate disable (get mfaToken)
  2. Verify and disable (send code + mfaToken, MFA disabled immediately)

New 3-step flow:

  1. Initiate disable - Request to disable MFA (returns mfaToken)
  2. Verify code - Validate user's TOTP/recovery code (returns confirmationToken)
  3. Confirm disable - Final confirmation to complete MFA disable (uses confirmationToken)

Changes include:

  • Refactored useRemoveMFA hook to implement 3-step disable flow
    • Separated verification and disable into distinct steps
    • Added new confirmationToken state management
    • Updated API endpoints: /initiate, /verify, /confirm
  • Updated RemoveMFA component UI to guide users through 3-step process
  • Fixed all 47 tests for the new flow architecture:
    • Updated test helpers with mockMFADisableVerifyCode() and mockMFADisableConfirm()
    • Fixed mock cleanup using vi.resetAllMocks() to prevent test interference
    • Comprehensive test coverage: initiate (6), verify (13), confirm (3), state management (4), loading states (1), edge cases (4), utils (14)
  • Added proper error handling at each step with session expiration checks
  • Maintained backward compatibility with existing MFA setup flow

🪤 Peer Testing

Requires valid MFA-enabled test account

Test Case 1: Successful MFA Disable with TOTP Code

  1. Log in to an account with MFA enabled

  2. Navigate to Settings → Account Settings → Security

  3. Click "Disable Two-Factor Authentication"

  4. Enter your current TOTP code from authenticator app

  5. Confirm the disable action

    Expected outcome:

    • Step 1: Modal shows code input field
    • Step 2: After entering valid code, shows confirmation screen
    • Step 3: After confirming, MFA is disabled successfully
    • Success message displayed, modal closes
    • "Enable Two-Factor Authentication" option now available

Test Case 2: Invalid Code - Should Not Proceed to Confirmation

  1. Follow steps 1-3 from Test Case 1

  2. Enter an invalid TOTP code (e.g., 000000)

  3. Click submit/verify button

    Expected outcome:

    • Error message: "Invalid verification code. Please check your authenticator app and try again."
    • User remains at verification step (does NOT advance to confirmation screen)
    • Can retry with correct code without restarting flow
    • Session state maintained (mfaToken persists)

Test Case 3: Cancel on Confirmation Screen

  1. Follow steps 1-4 from Test Case 1 (enter correct TOTP code)

  2. Verify confirmation screen is displayed

  3. Click "Cancel" button on confirmation screen

    Expected outcome:

    • Modal closes without disabling MFA
    • MFA remains enabled on the account
    • User can restart the disable flow if needed
    • No API call made to /confirm endpoint

Test Case 4: Multiple Invalid Attempts Error Handling

  1. Follow steps 1-3 from Test Case 1

  2. Enter invalid TOTP code (e.g., 000000)

  3. Repeat 5+ times

    Expected outcome:

    • After max attempts, error message: "Too many invalid attempts. Please try again later."
    • User remains at verification step
    • Session state maintained (no need to restart flow)
    • Can retry after cooldown period

Test Case 5: Disable with Recovery Code

  1. Follow steps 1-3 from Test Case 1

  2. Click "Use recovery code instead"

  3. Enter a valid recovery code (format: XXXXX-XXXXX)

  4. Confirm the disable action

    Expected outcome:

    • Recovery code accepted
    • Proceeds to confirmation step
    • MFA disabled successfully

Test Case 6: Session Expiration

  1. Start the disable flow

  2. Wait for session to expire (or simulate timeout)

  3. Attempt to proceed

    Expected outcome:

    • Error message: "Your verification session has expired. Please start over."
    • User needs to restart the disable flow

Test Case 7: Network Error Handling

  1. Start the disable flow

  2. Disconnect network or use browser dev tools to simulate offline

  3. Attempt to submit verification code

    Expected outcome:

    • Error message: "Network error. Please check your connection and try again."
    • Can retry once connection restored

✏️ Notes

Breaking Changes:

  • API endpoints updated from 2-step to 3-step flow
  • verifyAndDisable method replaced with verifyCode + confirmDisable
  • Backend must support new 3-step endpoints before merging

Test Coverage:

  • All 47 tests passing
  • Comprehensive coverage of success paths, error scenarios, and edge cases
  • Tests validate proper state management across all 3 steps
  • Validates that invalid codes do not advance to confirmation screen
  • Confirms cancellation on confirmation screen does not disable MFA

Related:

  • Depends on backend implementation of 3-step MFA disable endpoints

- Add comprehensive MFA translation keys to app-en.json
- Add French translations for all MFA features to app-fr.json
- Includes translations for:
  - MFA setup (QR code and manual setup flows)
  - Recovery codes management
  - Authenticator app configuration
  - Identity confirmation dialogs
  - MFA removal flow
  - All button labels and loading states
- Replace hardcoded strings with translation keys:
  - Account Settings title (mfa.accountSettings)
  - Profile section (mfa.profile)
  - Email label (mfa.email)
  - Two-factor authentication title and description
  - Authenticator app title and description
  - Enabled badge (mfa.enabled)
  - Add/Remove buttons (mfa.buttons.add/remove)
  - Recovery options header (mfa.recoveryOptions)
  - Recovery codes title and description
  - View button (mfa.buttons.view)
  - Success message for MFA removal (mfa.remove.successMessage)
- Import useTranslation hook from react-i18next
- Replace hardcoded strings:
  - Modal title (mfa.recoveryCodes.saveTitle)
  - Description paragraphs (saveDescription1, saveDescription2)
  - Button labels (mfa.buttons.savedCodes, downloadCodes)
- Replace hardcoded strings:
  - Modal title (mfa.setup.scanTitle)
  - Description (mfa.setup.scanDescription)
  - Continue button (mfa.buttons.continue)
  - Can't scan link (mfa.setup.cantScanQR)
- Replace hardcoded strings:
  - Modal title (mfa.setup.manualTitle)
  - Description (mfa.setup.manualDescription)
  - Instructions (mfa.setup.manualInstructions)
…lation keys

VerificationForm:
- Replace button labels with translation keys (mfa.buttons.continue, back)

SecretKeyDisplay:
- Replace copy/copied tooltips with translation keys (mfa.secretKey.copy, copied)
ViewRecoveryCodes:
- Replace title and description with translation keys

RemoveMFA:
- Replace title and description with translation keys

RemoveMFAConfirmation:
- Replace all text (title, message, buttons)

ConfirmIdentityVerificationCode:
- Replace placeholder, button labels, and link text

ConfirmIdentityRecoveryCode:
- Replace all text (title, description, placeholder, buttons)
- Added Noto Sans as variables.font.family.input
- Replaced all hardcoded Moderat with appropriate font.family properties
- Replaced all hardcoded Noto Sans with font.family.input
- Fixed incorrect variables.font.family object references
- Follows repo pattern

Files modified:
- AccountSettings.styles.ts
- AccountTab.styles.ts
- MFAManualSetup.styles.ts
- MFARecoveryCodes.styles.ts
- MFASetup.styles.ts
- RemoveMFAConfirmation.styles.ts
- MFADialog.styles.ts
- font.ts
- Follows repo pattern of using centralized SVG sprite
- Ensures consistency across the application

Files modified:
- MFARecoveryCodes.tsx
- MFASetup.tsx
- MFAManualSetup.tsx
- ConfirmIdentityRecoveryCode.tsx
- SecretKeyDisplay.tsx

Files deleted:
- CloseIcon.tsx
- CheckIcon.tsx
- Replace #ba1a1a with variables.palette.error40
- Replace rgba(186, 26, 26, 0.08) with error40 + hex alpha (14 = 8% opacity)
- Replace rgba(186, 26, 26, 0.3) with error40 + hex alpha (4D = 30% opacity)
- Replace rgba(255, 255, 255, 0.9) with white_alpha50
- Replace rgba(0, 0, 0, 0.5) with black + hex alpha (80 = 50% opacity)
- Replace hardcoded Toast colors with palette variables
- Fix incorrect variables.font.family object reference in MFADialog

Files modified:
- MFADialog.styles.ts
- RemoveMFAConfirmation.styles.ts
- AccountTab.styles.ts
- MFASetup.styles.ts
- Toast.tsx
- Create Toast.styles.ts following repo pattern
- Replace inline style object with StyledToast component
- Convert fontSize from px to rem (14px → 1.4rem)
- Use variables.font.weight.regular instead of hardcoded 400
- Improve box-shadow formatting for better readability
- Fix bug where MFA was disabled immediately after code verification
- Now MFA is only disabled after user confirms on final confirmation modal
- Store verification code temporarily and only call API on final confirm
- Ensures cancel button on confirmation modal actually prevents MFA removal
- Create centralized mock data file for MFA tests
- Include mock provisioning URIs, verification codes, and recovery codes
- Add mock API responses for success and error scenarios
- Include mock tokens for download and verification flows
- Provide mock error responses matching backend formats
- Create reusable helper functions for MFA test setup
- Add API mock helpers (success and error scenarios)
- Include utilities for mocking MFA setup, verification, and removal flows
- Add helpers for recovery codes viewing and downloading
- Provide common callback mock creators
- Add API call verification utilities

These helpers reduce duplication and improve test readability.
Add comprehensive test suite for MFA input handler hook:
- Test digit filtering (remove letters, special chars, spaces)
- Test length limiting (reject >6 digits, accept <=6)
- Test error clearing behavior
- Test combined operations (filtering + error clearing)
- Test progressive typing and blocking after 6 digits

Coverage: 12 tests
Focus: Input sanitization, validation, and user interaction flows
- 22 comprehensive tests covering all error scenarios
- Validates proper error message translation for users
- Add comprehensive tests for MFA disable hook
- Cover initiate disable flow with success and error cases
- Test verify and disable with all error scenarios
- Test state management and loading states
- Test edge cases (empty code, rapid calls, missing token)
- Add tests for useViewRecoveryCodes hook
- Test TOTP and recovery code verification flows
- Cover error handling (400, 403, 404, 429, network errors)
- Add helper functions: mockMFAViewCodesInitiate and mockMFAViewCodesVerify
- All tests passing (16/16)
- Fix modal closing on download button click
- Enforce downloadToken requirement
- Remove frontend-only download option
- Append download link to drawer element instead of document.body
- Prevents ClickAwayListener from detecting click as outside event
- Add session management state (mfaToken, sessionInitialized)
- Initiate MFA session once when modal opens via useEffect
- Reuse existing mfaToken for both TOTP and recovery code verification
- Wrap initiateSession in useCallback for stable reference
- Add useRef to prevent duplicate API calls in StrictMode
- Add resetSession function to clear all session state
- Call resetSession when modal closes
- Ensures new MFA session token on every 'View Recovery Codes' click
- Add onRetry prop to ConfirmIdentityVerificationCode and ConfirmIdentityRecoveryCode
- Disable input field when onRetry is provided
- Replace Continue/Back buttons with Try Again button
- Update MFADisableVerifyResponse to return codeValidated and confirmationToken
- Add MFADisableConfirmRequest type for final confirmation step
- Add MFADisableConfirmResponse type for disable completion
@aws-amplify-us-east-1
Copy link

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-2190.d2ccder08v9rmu.amplifyapp.com

@sricharan-varanasi sricharan-varanasi changed the title Mfa disable refactor refactor: Mfa disable refactor (M2-10278) Jan 5, 2026
@sricharan-varanasi sricharan-varanasi marked this pull request as ready for review January 5, 2026 14:30
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