Repository navigation
Add Sign out escape hatch to required 2FA overlay - #100077
luacmartins merged 22 commits into
Conversation
Add a horizontal Sign out + Enable 2FA button row on the blocking require-2FA overlay. Sign out uses the same confirm modal as Settings, then redirects to sign-in so users can switch accounts. Fixes Expensify#91985
|
@thelullabyy Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 230816db4a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Extract Settings sign-out flow (receipt save, offline/GPS warnings) into InteractiveSignOut and wire the overlay to it so sign-out matches Settings. Rename overlay handlers per STYLE.md (enableTwoFactorAuth, confirmSignOut).
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 58740e275a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
When the require-2FA overlay already confirmed sign-out, do not show the same generic offline warning again; still show GPS and receipt prompts. Remove unused InteractiveSignOutParams export for knip.
JmillsExpensify
left a comment
There was a problem hiding this comment.
Big fan of this one. I've hit it myself and it's really rough to go through. Testing steps LGTM
|
@neerajbachani Please check pipelines |
|
@thelullabyy CI was failing on unrelated checks ( |
|
Hey @thelullabyy While QA’ing and recording the sign-out escape hatch on latest Issue: “Stay ahead of the deadline” ( Repro:
Note: This doesn’t appear related to this PR (no changes to trial reminder code). It looks like existing behavior in Happy to file a separate issue (or follow-up PR) if helpful didn’t include a fix here to keep this PR scoped to the sign-out hatch. Screen.Recording.2026-09-04.at.12.13.43.AM.mov |
|
@neerajbachani Let's add all other platforms recordings, I see you attached only MacOS Chrome recording |
I don't think that's ok—feel pretty unacceptable to truncate an action button haha. I think we should change the body text to say |
|
Can we just shorten the button label to be "Enable" or "Enable 2FA"? |
|
@shawnborton JINX!!!! |
|
JINX! Also, where is the Figma mock for this? I wonder if these buttons should just hug and not be fixed width anyways? |
I don't think there is one, but I think this layout is essentially based off of the layout in this Figma file. But I agree, they could just be centered and hug their contents if the labels were Sign out & Enable 2FA. |
|
@thelullabyy Thanks for the video. I've changed the overlay's Sign out to use exactly the same flow as Settings: no confirm when you are online, and the confirm only when you are offline, have a GPS trip running, or have unsaved receipts. That removes the confirm modal that stayed on screen during online sign-out. If you test offline on local web, note that the dev proxy (web/proxy.ts) never answers when the upstream request fails. LogOut stays pending until the connection returns, then the redirect happens. On staging/production, or with USE_WEB_PROXY=false, the request fails right away and you land on the login page. Settings works the same way. No backend change is needed. |
|
I think we should fix the duplicated modal issue here instead of removing that modal @neerajbachani Please revert the changes Besides, even if we remove the modal, it is still missing loading indicator Screen.Recording.2026-09-29.at.01.04.11.mov |
|
@thelullabyy We didn't remove the confirm modal, it still shows. The overlay's Sign out now follows the same rules as Settings > Sign out on
Both screens call the same You're right about the loading indicator. The button had If you'd still prefer the overlay to always show a confirmation, I can add that back. From a product perspective, though, I don't think an additional confirmation is necessary on the first screen since the user has explicitly chosen to sign out. This is also consistent with the existing Settings > Sign out behavior. The duplicate-modal issue is covered either way now, since the button remains in the loading state until sign-out finishes. Screen.Recording.2026-09-28.at.11.15.49.PM.mov |
|
This PR adds a new |
|
You can go ahead and merge @luacmartins |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppiOS: mWeb SafariMacOS: Chrome / Safari |
|
🚧 luacmartins has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/luacmartins in version: 9.4.96-0 🚀
|
|
No help site update needed: no article in |
|
Deploy Blocker #102609 was identified to be related to this PR. |
|
🚀 Deployed to staging by https://github.com/luacmartins in version: 9.4.99-0 🚀
|
|
No help site update needed. No article under What I checked
|
|
🚀 Deployed to production by https://github.com/Julesssss in version: 9.4.99-2 🚀
Bundle Size Analysis (Sentry): |


Add a horizontal Sign out + Enable 2FA button row on the blocking require-2FA overlay. Sign out uses the same confirm modal as Settings, then redirects to sign-in so users can switch accounts.
$ #91985
Explanation of Change
This follow-up to #94486 adds the Sign out escape hatch that was de-scoped from the original PR. When the full-screen require-2FA overlay is shown, users now see a horizontal row with Sign out (left) and Enable 2FA (right). Tapping Sign out opens the same confirmation modal used on the Settings page; confirming calls
signOutAndRedirectToSignIn()so users who signed into the wrong account can switch without completing 2FA setup. Enable 2FA behavior is unchanged.For copilot delegates, Leave account uses
disconnect()instead of sign-out. GPS points when leaving during an active trip are read once viaconnectWithoutView(see below) to avoid subscribing to fullGPS_DRAFT_DETAILSand re-rendering Settings on every location tick.Onyx.connectWithoutView approval
useSignOutuses a one-shotreadOnce()helper (connectWithoutView+ immediatedisconnect) to readGPS_DRAFT_DETAILSonly after the user confirms leaving a delegated account during an active GPS trip. Points are needed inside an async post-modal callback, not during render, souseOnyxwould either require a full-object subscription (re-renders Settings on every GPS point merge) or agpsPointsselector (same problem). The connection is ephemeral: connect, read once, disconnect on user action only.Slack discussion (@frontend-performance approval): Pending — will add permalink after posting in #frontend-performance; Onyx review requested in PR comment
Fixed Issues
$ #91985
PROPOSAL: #91985 (comment)
Tests
Offline tests
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Screen.Recording.2026-09-08.at.11.47.11.PM.mov
Android: mWeb Chrome
WhatsApp.Video.2026-09-08.at.23.54.29.mp4
iOS: Native
Screen.Recording.2026-09-08.at.11.39.41.PM.mov
iOS: mWeb Safari
Screen.Recording.2026-09-08.at.11.52.06.PM.mov
MacOS: Chrome / Safari
Screen.Recording.2026-08-11.at.12.33.03.AM.online-video-cutter.com.mp4