Skip to content

fix(web): MFA auth flow + Security tab display bugs#210

Merged
FSM1 merged 6 commits into
mainfrom
fix/mfa-auth-flow-broken
Feb 26, 2026
Merged

fix(web): MFA auth flow + Security tab display bugs#210
FSM1 merged 6 commits into
mainfrom
fix/mfa-auth-flow-broken

Conversation

@FSM1

@FSM1 FSM1 commented Feb 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • MFA auth flow (3 bugs): device factor auto-detect on re-login, recovery race condition with session restore, device approval 401 from missing access token
  • Security tab display (4 bugs): factor type detection via tssShareIndex normalization, flat metadata extraction from Web3Auth JSON, device metadata in recovery flow, lastActive registry map broadened to include pending devices
  • All 7 bugs traced to Web3Auth SDK internals (enableMFA defaults, addFactorDescription spread behavior, handleExistingUser not checking localStorage)

Test plan

  • Enable MFA on fresh account, logout, re-login — should auto-detect device factor (no "missing shares")
  • Sign in with recovery phrase — should complete auth and land on vault (no redirect loop)
  • New browser triggers device approval — should not 401
  • After recovery sign-in, Security tab shows "recovery phrase active" (not "no recovery phrase")
  • Device list shows browser name (not "Unknown device")
  • Device last active shows "just now" for current device (not "unknown")
  • Factor count matches visible UI elements

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed multiple MFA issues: session restoration timing, device-factor auto-detection, token race conditions, and device status/display inaccuracies.
    • Improved device registry to show all non-revoked devices and more accurate activity indicators.
    • Made approval/request flow wait for authentication token and added reliable retry/cleanup behavior.
    • Strengthened device-factor recovery and factor parsing for more reliable logins.
  • Documentation

    • Added a detailed incident/resolution document describing observed MFA issues and fixes.

FSM1 and others added 2 commits February 26, 2026 22:10
…pproval 401

Three interrelated MFA bugs fixed:

1. Device factor not auto-detected on re-login: Web3Auth SDK's
   handleExistingUser() does not check localStorage for device factor
   after hashedShare deletion (post-MFA). Added explicit getDeviceFactor()
   + inputFactorKey() in doLoginWithCoreKit REQUIRED_SHARE branch.

2. Recovery redirects to login: inputFactorKey() called syncStatus()
   prematurely, transitioning React context to LOGGED_IN before backend
   auth completed. Session restoration effect fired, refresh failed,
   coreKitLogout() undid recovery. Fix: defer syncStatus() to
   completeRequiredShare() after backend auth succeeds.

3. Device approval 401: DeviceWaitingScreen fired requestApproval() on
   mount before temp access token was stored (race between React flush
   and async continuation). Fix: wait for accessToken in auth store
   before firing request.

Debug session: .planning/debug/resolved/mfa-auth-flow-broken.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 0d55c2e26968
…lastActive

- Fix getFactors() to normalize factor types via tssShareIndex when
  module is "Other" (enableMFA default): index 2→DeviceShare, 3→SeedPhrase
- Fix getFactors() to extract additionalMetadata from flat JSON structure
  (Web3Auth spreads it at top level, not nested)
- Add deviceId/browserName metadata to recoverWithMnemonic() createFactor
- Pass shareDescription to enableMFA() for correct recovery factor tagging
- Broaden AuthorizedDevices registry map to include pending devices (not
  just authorized), so recovery-created devices show lastSeenAt
- Fall back to "just now" for current device when registry hasn't loaded

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 4ffcfbbacd4a
@coderabbitai

coderabbitai Bot commented Feb 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@FSM1 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 15 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4c73ee1 and 7dbdf5b.

📒 Files selected for processing (1)
  • apps/web/src/components/mfa/DeviceWaitingScreen.tsx

Walkthrough

Seven interrelated MFA bugs were diagnosed and fixed: device-factor auto-detection during REQUIRED_SHARE login, Core Kit ↔ React state sync, token-gated approval request initiation, device registry inclusion of non-revoked devices and lastSeen rendering, factor description/type normalization and metadata extraction, device registry handling (authorized vs pending), and related TypeScript/ESLint cleanups.

Changes

Cohort / File(s) Summary
Planning Documentation
.planning/debug/resolved/mfa-auth-flow-broken.md
Adds a new resolution document that lists seven MFA bugs, their symptoms, root-cause analysis, code fixes, and verification results.
Device Registry & Listing
apps/web/src/components/mfa/AuthorizedDevices.tsx
Include all non-revoked devices (not only 'authorized') in registry mapping; adjust lastActive display to show 'just now' for current device and otherwise 'unknown'.
Approval Request Flow (UI)
apps/web/src/components/mfa/DeviceWaitingScreen.tsx
Delay firing approval request until accessToken is available; add requestFiredRef to throttle requests; start/clear countdown only when request triggers; cancel pending requests on unmount; support retry resets.
Auth Synchronization
apps/web/src/hooks/useAuth.ts
Call Core Kit syncStatus() after completing backend auth in REQUIRED_SHARE flow and include syncStatus in dependency arrays to ensure React state aligns with Core Kit state.
MFA Device Handling & Factor Parsing
apps/web/src/hooks/useMfa.ts
Add device helpers (getOrCreateDeviceIdentity, detectDeviceInfo); create device-aware share descriptions; expand getFactors parsing to extract systemFields, normalize factor types (DeviceShare, SeedPhrase), and capture additional string metadata.
Web3Auth Core Login Flow
apps/web/src/lib/web3auth/hooks.ts
On CoreKit REQUIRED_SHARE, attempt local device-factor recovery: read getDeviceFactor(), input factor via inputFactorKey (BN), commit on LOGGED_IN, call syncStatus(), and handle errors gracefully.
CI Detection
.github/workflows/ci.yml
Narrowed change-detection globs to explicit includes and added additional config/lock files; removed some excludes so MD/LICENSE count as changes.

Sequence Diagrams

sequenceDiagram
    participant User
    participant LoginFlow as Web3Auth Login
    participant LocalStorage as Local Storage
    participant CoreKit as Core Kit SDK
    participant Backend as Backend Auth
    participant ReactState as React State

    User->>LoginFlow: Initiate login
    LoginFlow->>CoreKit: Check login status
    CoreKit-->>LoginFlow: REQUIRED_SHARE
    LoginFlow->>LocalStorage: getDeviceFactor()
    alt device factor exists
        LocalStorage-->>LoginFlow: deviceFactorHex
        LoginFlow->>CoreKit: inputFactorKey(deviceFactor)
        CoreKit->>Backend: Validate factor
        Backend-->>CoreKit: Validation result
        alt valid
            CoreKit-->>LoginFlow: LOGGED_IN
            LoginFlow->>CoreKit: commit()
            CoreKit-->>LoginFlow: committed
            LoginFlow->>ReactState: syncStatus()
            ReactState-->>LoginFlow: updated
            LoginFlow-->>User: success
        else invalid
            CoreKit-->>LoginFlow: error
            LoginFlow-->>LoginFlow: remain REQUIRED_SHARE
        end
    else no device factor
        LocalStorage-->>LoginFlow: null
        LoginFlow-->>LoginFlow: true REQUIRED_SHARE
    end
Loading
sequenceDiagram
    participant Component as Device Waiting Screen
    participant Token as Auth Store (accessToken)
    participant Effect as Effect Hook
    participant ApprovalAPI as Approval API
    participant UI as Countdown Timer UI

    Component->>Effect: mount
    Effect->>Token: read accessToken
    alt token not available
        Token-->>Effect: null
        Effect->>Effect: wait for token
    else token available
        Token-->>Effect: accessToken
        Effect->>Effect: check requestFiredRef
        alt not fired
            Effect->>Effect: set requestFiredRef = true
            Effect->>ApprovalAPI: fire approval request
            ApprovalAPI-->>Effect: request started
            Effect->>UI: start countdown
            UI-->>Component: render timer
        else already fired
            Effect->>Effect: skip
        end
    end
    Component->>Component: unmount
    Component->>UI: clear countdown interval
    Component->>ApprovalAPI: cancelRequest()
    ApprovalAPI-->>Component: cancelled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: fixing MFA authentication flow bugs and Security tab display issues, which are the primary objectives documented in the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/mfa-auth-flow-broken

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/components/mfa/DeviceWaitingScreen.tsx (1)

69-73: ⚠️ Potential issue | 🟡 Minor

Reset requestFiredRef in handleRetry to allow auto-request re-triggering on retry

When the user clicks retry, handleRetry() should reset requestFiredRef.current = false along with the other state. Currently, if the requestApproval function reference changes after a retry (e.g., due to parent re-renders), the first useEffect won't re-fire the auto-request because requestFiredRef.current is still true. This violates the expected behavior of the retry action: allowing a fresh approval flow.

const handleRetry = useCallback(() => {
  requestFiredRef.current = false;
  startTimeRef.current = Date.now();
  setCountdown(APPROVAL_TTL_MS);
  requestApproval();
}, [requestApproval]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/mfa/DeviceWaitingScreen.tsx` around lines 69 - 73,
handleRetry currently resets startTimeRef and countdown and calls
requestApproval but does not reset requestFiredRef, so the auto-request
useEffect may not re-trigger after a retry; update handleRetry to set
requestFiredRef.current = false before resetting startTimeRef.current and
calling requestApproval (ensure you reference handleRetry, requestFiredRef,
startTimeRef, setCountdown, requestApproval, and APPROVAL_TTL_MS) so a retry
allows a fresh approval flow to be auto-fired.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/web/src/components/mfa/DeviceWaitingScreen.tsx`:
- Around line 69-73: handleRetry currently resets startTimeRef and countdown and
calls requestApproval but does not reset requestFiredRef, so the auto-request
useEffect may not re-trigger after a retry; update handleRetry to set
requestFiredRef.current = false before resetting startTimeRef.current and
calling requestApproval (ensure you reference handleRetry, requestFiredRef,
startTimeRef, setCountdown, requestApproval, and APPROVAL_TTL_MS) so a retry
allows a fresh approval flow to be auto-fired.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee2e01f and 0e5698d.

📒 Files selected for processing (6)
  • .planning/debug/resolved/mfa-auth-flow-broken.md
  • apps/web/src/components/mfa/AuthorizedDevices.tsx
  • apps/web/src/components/mfa/DeviceWaitingScreen.tsx
  • apps/web/src/hooks/useAuth.ts
  • apps/web/src/hooks/useMfa.ts
  • apps/web/src/lib/web3auth/hooks.ts

FSM1 and others added 3 commits February 27, 2026 00:05
Without this reset, if requestApproval's reference changes after retry
(e.g. parent re-render), the auto-fire useEffect won't re-trigger
because requestFiredRef.current stays true from the initial request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 442c2624f0ae
dorny/paths-filter@v3 was leaking the src filter's ** glob pattern
into the desktop filter evaluation, causing desktop builds (Windows +
macOS cargo check + Tauri build) to run on every PR regardless of
whether desktop files changed.

Replace ** with explicit directory patterns (apps/, packages/,
tee-worker/, tests/, .github/) to isolate the filters correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 8b7f503d9e83

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/mfa/DeviceWaitingScreen.tsx`:
- Around line 26-31: The effect that creates the polling interval can leak if it
sets a new interval while an old one is still active; before creating a new
interval in DeviceWaitingScreen (the logic that uses countdownRef,
requestFiredRef and requestApproval), defensively clear any existing interval by
checking countdownRef.current and calling clearInterval on it, then assign the
new interval id to countdownRef.current; also ensure you clear
countdownRef.current in the effect cleanup and when handleRetry resets
requestFiredRef so you don't leave a stale interval running.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e5698d and 4c73ee1.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • apps/web/src/components/mfa/DeviceWaitingScreen.tsx

Comment thread apps/web/src/components/mfa/DeviceWaitingScreen.tsx
Defensive fix: if handleRetry resets requestFiredRef and the effect
re-fires (e.g. requestApproval reference changes), a new setInterval
was created without clearing the previous one. Clear any existing
interval at the start of the effect to prevent leaks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 428a2491751c
@FSM1 FSM1 enabled auto-merge (squash) February 26, 2026 23:25
@FSM1 FSM1 merged commit 9fd64d1 into main Feb 26, 2026
14 checks passed
FSM1 added a commit that referenced this pull request Feb 27, 2026
Verify and close two active debug sessions after confirming all
issues are fixed on main with commit/PR cross-references.

corekit-auth-uat: 4 issues resolved (ISSUE-001 tab crash, ISSUE-003
JWKS persistence, ISSUE-004 SecurityTab wiring, ISSUE-002 documented).
Added post-session follow-up fixes (#205, #210, #213) and E2E coverage
mapping for 19 previously-skipped UAT test cases.

mfa-banner-footer-overlap: grid-area fix (#143) and z-index follow-up
verified on main with file/line references.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b1ae6e4f2098
FSM1 added a commit that referenced this pull request Feb 27, 2026
Verify and close two active debug sessions after confirming all
issues are fixed on main with commit/PR cross-references.

corekit-auth-uat: 4 issues resolved (ISSUE-001 tab crash, ISSUE-003
JWKS persistence, ISSUE-004 SecurityTab wiring, ISSUE-002 documented).
Added post-session follow-up fixes (#205, #210, #213) and E2E coverage
mapping for 19 previously-skipped UAT test cases.

mfa-banner-footer-overlap: grid-area fix (#143) and z-index follow-up
verified on main with file/line references.


Entire-Checkpoint: b1ae6e4f2098

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@FSM1 FSM1 deleted the fix/mfa-auth-flow-broken branch March 4, 2026 01:16
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.

1 participant