decentraland / decentraland/auth
Stale WalletConnect state pops the wallet chooser over the Apple sign-in on mobile auth (page-load session restore is not passive)
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 3
- Avg merge
- 8h 13m
- Merged PRs (30d)
- 37
Description
## Problem
Logging in from the mobile explorer with Sign in with Apple can show two prompts at once: the Magic "Sign in with Apple" sheet and, stacked behind it, the AppKit "Connect Wallet" chooser that nobody asked for. Reproduced on iOS (2026-08-03).
Repro:
1. In the mobile explorer, sign in with WalletConnect. This leaves `decentraland-connect-storage-key` = `WALLET_CONNECT_V2` in the in-app browser's localStorage.
2. Let the WalletConnect session die — expiry, wallet-side disconnect, partial storage cleanup. The key ingredient is connection data that still points at WC while the wc session is no longer alive.
3. Open the explorer again and pick Sign in with Apple, which lands on `/auth/mobile?provider=apple`.
4. The Apple OAuth sheet and the WalletConnect chooser appear at the same time.
The WalletConnect modal is completely invalid in that context: the user explicitly asked for Apple, and nothing on screen triggered a wallet connection.
## Root cause
Two independent things fire on page load and collide:
1. `MobileAuthPage` sees `provider=apple` and auto-starts the Magic OAuth flow (`src/components/Pages/MobileAuthPage/MobileAuthPage.tsx`, the `initialize` effect → `initiateAuth`). This one is intended.
2. `ConnectionProvider` wraps every route in `src/main.tsx`, and on mount calls `connection.tryPreviousConnection()` to restore the previous session (`src/shared/connection/ConnectionProvider.tsx` → `src/shared/connection/connection.ts`). That call is meant to be a passive restore, but for WalletConnect it is not: decentraland-connect's `WalletConnectV2Connector.activate()` checks `appKit.getAccount()`, and when the restored session is **not** alive it calls `openModalAndWaitForConnection()` — the background restore opens the Connect Wallet modal.
`MobileAuthPage` does clear stale auth state on startup — `magic.user.logout()`, the Thirdweb in-app wallet, `single-sign-on-*` keys — but never touches the decentraland-connect connection data, so a prior WC login leaves the trap armed.
Note the failure needs a *dead* session: a live WC session restores silently (that silent reuse is what the request-flow handoff relies on), which is why the bug looks intermittent.
## When it was introduced
Not recent. Archaeology:
- 2025-10-08 — #199 made `LoginPage` probe `tryPreviousConnection()` on mount (via `useAuthFlow`). First time a login-facing page could hit the WC restore path on load.
- 2026-01-05 — #238 bumped decentraland-connect to v11, the AppKit rewrite with the current modal-on-dead-session `activate()`. The pre-AppKit v9 connector had the same trap in another shape: `provider.enable()` with `showQrModal: true` would pop the old QR modal.
- 2026-01-16 — #246 (auto-login via `loginMethod=` param) and #245 (`MobileAuthPage`) landed. From here the double prompt was reachable on the web login page.
- 2026-03-11 — #347 wrapped **all** routes, including `/auth/mobile`, in `ConnectionProvider` with its unconditional page-load probe. This is when the mobile flow above became broken.
The July changes are innocent: #438 deliberately changed only the explicit-click path, and its commit message states the automatic-restore path was left untouched.
## Fix direction
The page-load restore should be truly passive:
- Skip the WalletConnect restore when AppKit reports no live session, instead of opening the modal. Cleanest as a "silent" activate mode in decentraland-connect that throws rather than prompts, with `getCurrentConnectionData` treating that as "not connected".
- Independently, auth entry pages carrying an explicit login intent (`provider=` / `loginMethod=` params) arguably should not attempt to resurrect a previous session at all — if the app is sending the user to sign in with Apple, there is no reason to restore a WalletConnect session under it.
- `MobileAuthPage`'s startup cleanup could also clear the decentraland-connect connection data along with the Magic/Thirdweb/SSO state it already clears, since that page always starts a fresh login.
## Related
- #438 fixed the inverse UX problem on the explicit-click path (session reuse suppressing the chooser); this issue is about the automatic-restore path doing the opposite (the chooser appearing unprompted).
- decentraland-connect 12.1.1 (decentraland/decentraland-connect#124) scoped storage clearing to WC-only disconnects, which makes leftover WC state in this flow more likely to survive, not less.
Contributor guide
Research direction
Start with src/main.tsx and src/shared/connection/ConnectionProvider.tsx, then trace connection.tryPreviousConnection() into src/shared/connection/connection.ts and the WalletConnect activation path. Compare that with the initialize effect and initiateAuth in src/components/Pages/MobileAuthPage/MobileAuthPage.tsx. Done means an Apple sign-in does not open an unprompted wallet chooser while passive session restoration still works for a live WalletConnect session.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- authentication, mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100