Preserve any passkeys in onyx when clearing the cache or logging out#86059
Preserve any passkeys in onyx when clearing the cache or logging out#86059rafecolton merged 6 commits intomainfrom
Conversation
dariusz-biela
left a comment
There was a problem hiding this comment.
The code looks good.
I checked it, and it covers all cases where onyx is cleared that weren't previously handled by KEYS_TO_PRESERVE.
I'm waiting for the merge PR with the manual tests [HOLD react-native-onyx#759]
|
@ShridharGoel 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✅ Changes either increased or maintained existing code coverage, great job!
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb14a626cc
ℹ️ 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".
|
Looks like some kind of state issue in the case where the user signs out, then immediately signs back in, without refreshing the page. Registration gets stuck on the soft prompt. Perhaps we need to preserve macos chromeapp.pr.first.run.after.login.gets.stuck.mp4 |
|
Hmm, I am getting the same error 🤔 |
The first time through this flow, `deviceBiometricsState?.hasAcceptedSoftPrompt` hasn't loaded from onyx yet, so we re-enter the soft prompt, but then `softPromptApproved` _has_ loaded and prevents the button from showing. To fix this, use `softPromptApproved` as a gate too, so the first time through, we don't get stuck
|
Alright @chuckdries, I pushed a fix to the bug you described. Claude claims the same issue existed for mobile native and the fix applies there too, but I was not able to reproduce the issue on Android, only on web with passkey. Edit: I'm not sure it's quite right actually. I think we shouldn't be showing the soft prompt at all once you're registered unless you fully uninstall the app / manually delete the passkey entry out of onyx. Exploring a little more. |
|
Here's a video showing the bug is fixed: Screen.Recording.2026-03-23.at.5.56.21.PM.mov |
| // delete and reinstall the app. Their keys are preserved in the secure store, but they'll be shown | ||
| // the "do you want to enable Face ID" system prompt again, so we want to ensure they see the soft | ||
| // prompt first. | ||
| if (!deviceBiometricsState?.hasAcceptedSoftPrompt && !softPromptApproved) { |
There was a problem hiding this comment.
I checked it, and the fix works.
However, I noticed one more bug.
When we have passkeys registered and log in again, a soft prompt appears, but according to the design, we should be taken directly to the authorization screen.
There was a problem hiding this comment.
In my opinion, we should add something like this:
const areLocalCredentialsKnownToServer = await biometrics.areLocalCredentialsKnownToServer();
const isRegistrationRequired = !areLocalCredentialsKnownToServer && !isRegistrationComplete;
if (isRegistrationRequired && !deviceBiometricsState?.hasAcceptedSoftPrompt && !softPromptApproved) {
chuckdries
left a comment
There was a problem hiding this comment.
I agree with Dariusz that ideally we would not show the soft prompt to the user in this flow, but I don't think it's that big a deal and it fixes the bug, so I'm approving
Reviewer Checklist
Screenshots/VideosAndroid: mWeb Chromepasskey.preserve.works.android.chrome.mp4iOS: mWeb Safaripasskey.preserve.works.ios.safari.mp4MacOS: Chrome / Safaripasskey.preserve.works.chrome.mp4 |
|
@chuckdries @dariusz-biela is this ready to be merged? (asking as we have perf improvements held on this) |
|
@Julesssss We talked it over in slack and Rafe wanted to see if we could improve the behavior for this flow. Are you just holding to bring in the Onyx upgrade? We're happy to rebase if someone else upgrades Onyx to or above the version we need, no need to hold on that on our account. |
|
We can also merge and make the other changes as a follow up |
|
I'd rather not rebase over other onyx changes in case those end up getting reverted |
|
Since this solves the core issue and other PRs are blocked waiting to bump the version of react-native-onyx, I'm going to merge this now and address Dariusz's feedback as a separate, followup issue. |
|
🚧 @rafecolton has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |

Hold for Expensify/react-native-onyx#759
Explanation of Change
Preserve passkey credential IDs when clearing Onyx cache. This matches the behavior of mobile native keys, which are similarly preserved through logout. The values are verified server-side and attached to an accountID, so there is no risk in storing them or in them being accessed by another account. This alleviates the need for frequent re-registration on the same browser.
cc @chuckdries @dariusz-biela
Fixed Issues
$ #86005
PROPOSAL: N/A
Tests
Testflow in the Troubleshoot menu on webTestbutton prompts for authentication only once (i.e. authenticate flow, not registration flow)Offline tests
N/A, online only
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, 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.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
MacOS: Chrome / Safari
Screen.Recording.2026-03-22.at.2.14.23.PM.mov