Skip to content

fix(contacts): resolve seed-login race conditions in contacts and payment linking#831

Merged
bmc08gt merged 2 commits into
code/cashfrom
fix/seed-login-contacts-gate
Jun 2, 2026
Merged

fix(contacts): resolve seed-login race conditions in contacts and payment linking#831
bmc08gt merged 2 commits into
code/cashfrom
fix/seed-login-contacts-gate

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Contacts gate re-appearing after seed login: The send flow showed the contacts permission gate again after it was already granted during onboarding. Root causes: performSync() empty-contacts path didn't set hasEverSynced, the onboarding permission screen never triggered a sync, and the Flipcash contacts discovery modal used an imperative currentStep check that missed already-settled state.
  • linkForPayment firing prematurely during onboarding: hasCompletedOnboarding() defaults to true on new devices (backward compat), causing AuthManager.login to set Ready immediately for interactive logins — before permission screens finished. This triggered onAppInForeground side effects too early. Additionally, overlapping onAppInForeground calls (auth-state observer + ON_RESUME lifecycle) raced into duplicate linkForPayment RPCs.

bmc08gt added 2 commits June 2, 2026 16:14
Three issues caused the contacts permission gate to re-appear in the
send flow after granting permission during seed restore onboarding:

1. performSync() empty-contacts early return did not set hasEverSynced,
   so the send flow gate condition still evaluated true.

2. The onboarding ContactPermissionStepContent only granted the OS
   permission and proceeded -- it never triggered contactCoordinator.sync().
   The send flow then saw no synced contacts and showed the gate again.

3. The Flipcash contacts discovery modal used an imperative currentStep
   check inside a filter on contactCoordinator.state, which only evaluated
   when contact state changed. If the state was already settled from the
   onboarding sync, the modal never fired. Replaced with combine() so
   changes to either source trigger re-evaluation.

Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
Interactive logins (seed input, deep link, credential picker) set
AuthState.Ready too early because hasCompletedOnboarding() defaults to
true for backward compat on new devices. This triggered onAppInForeground
-- and linkForPaymentIfNeeded -- while permission screens were still up.

Fix: for non-soft logins, always force completedOnboarding to false so
auth routes through Onboarding(PostAccessKey). The permissions flow sets
Ready on completion at the right time. Soft logins (app restart) still
trust the persisted flag.

Additionally, guard linkForPaymentIfNeeded with a Mutex so overlapping
calls from the auth-state observer and ON_RESUME lifecycle cannot race
into duplicate RPCs.

Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
@bmc08gt bmc08gt self-assigned this Jun 2, 2026
@github-actions github-actions Bot added type: fix Bug fix area: auth Login, session, access keys, identity area: onboarding labels Jun 2, 2026
@bmc08gt bmc08gt merged commit 25debb8 into code/cash Jun 2, 2026
3 checks passed
@bmc08gt bmc08gt deleted the fix/seed-login-contacts-gate branch June 2, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth Login, session, access keys, identity area: onboarding type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant