fix(expo): inline AuthView OAuth + Android sign-out state cleanup#8260
fix(expo): inline AuthView OAuth + Android sign-out state cleanup#8260chriscanin wants to merge 6 commits intomainfrom
Conversation
…password The inline AuthView embedded as a child UIHostingController in React Native's view hierarchy disrupts ASWebAuthenticationSession callbacks during OAuth flows. SSO from the forgot-password screen would silently fail because the OAuth callback couldn't properly update Clerk.shared.client in the embedded context. This changes ClerkAuthNativeView to present the AuthView as a full-screen modal (matching the working presentAuth() behavior) instead of embedding it inline. Also adds retry logic for modal presentation after sign-out to handle cases where a previous modal (e.g., UserProfileView) is still dismissing.
🦋 Changeset detectedLatest commit: 8a0597e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Replace the unconditional 0.3s asyncAfter delay with presentWhenReady(), which presents the auth modal synchronously when possible. The fixed delay caused a visible white flash on initial mount because ClerkAuthNativeView is an empty UIView while waiting to present. When a previous modal (e.g., UserProfileView sign-out) is still dismissing, use UIViewController.transitionCoordinator to wait for the animation to complete instead of polling. Falls back to a one-frame DispatchQueue.main.async retry only when no coordinator is available yet.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughiOS: inline auth view now presents the auth UI controller modally instead of embedding a Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
|
!snapshot |
This comment has been minimized.
This comment has been minimized.
Three related Android fixes for the inline AuthView: 1. Detect new sign-ins by session ID change (not null-to-value). ClerkAuthExpoView's initialSessionId is captured at construction, but the view can be instantiated before signOut has finished clearing local state, causing it to capture a stale session ID. Switching to ID inequality lets subsequent sign-ins fire the auth-completed event correctly. 2. Per-view ViewModelStore for ClerkAuthExpoView. The clerk-android AuthView's navigation ViewModel was scoped to the MainActivity, so its navigation state (e.g. "Get help" destination) persisted across mount/unmount cycles within the same activity. Each ClerkAuthExpoView instance now provides its own ViewModelStoreOwner so the AuthView gets a fresh ViewModel scope per mount. 3. Refresh client from server after sign-out. Clerk.auth.signOut() only clears the active session, not the in-progress Clerk.client.signIn. After sign-out (whether via the JS bridge signOut(), the inline UserProfile view, or the modal UserProfile activity), call Client.getSkippingClientId() to fetch a brand-new client. The skipping variant is required because Client.get() echoes back the same client_id header, returning the same client with the stale signIn still attached.
|
!snapshot |
|
Hey @chriscanin - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/agent-toolkit@0.3.12-snapshot.v20260408154640 --save-exact
npm i @clerk/astro@3.0.12-snapshot.v20260408154640 --save-exact
npm i @clerk/backend@3.2.8-snapshot.v20260408154640 --save-exact
npm i @clerk/chrome-extension@3.1.9-snapshot.v20260408154640 --save-exact
npm i @clerk/clerk-js@6.5.1-snapshot.v20260408154640 --save-exact
npm i @clerk/dev-cli@0.1.1-snapshot.v20260408154640 --save-exact
npm i @clerk/expo@3.1.9-snapshot.v20260408154640 --save-exact
npm i @clerk/expo-passkeys@1.0.10-snapshot.v20260408154640 --save-exact
npm i @clerk/express@2.1.0-snapshot.v20260408154640 --save-exact
npm i @clerk/fastify@3.1.10-snapshot.v20260408154640 --save-exact
npm i @clerk/hono@0.1.10-snapshot.v20260408154640 --save-exact
npm i @clerk/localizations@4.3.3-snapshot.v20260408154640 --save-exact
npm i @clerk/msw@0.0.10-snapshot.v20260408154640 --save-exact
npm i @clerk/nextjs@7.0.12-snapshot.v20260408154640 --save-exact
npm i @clerk/nuxt@2.1.2-snapshot.v20260408154640 --save-exact
npm i @clerk/react@6.2.1-snapshot.v20260408154640 --save-exact
npm i @clerk/react-router@3.0.12-snapshot.v20260408154640 --save-exact
npm i @clerk/shared@4.5.1-snapshot.v20260408154640 --save-exact
npm i @clerk/tanstack-react-start@1.0.12-snapshot.v20260408154640 --save-exact
npm i @clerk/testing@2.0.12-snapshot.v20260408154640 --save-exact
npm i @clerk/ui@1.4.1-snapshot.v20260408154640 --save-exact
npm i @clerk/upgrade@2.0.3-snapshot.v20260408154640 --save-exact
npm i @clerk/vue@2.0.11-snapshot.v20260408154640 --save-exact |
Description
Two related fixes for the inline
<AuthView>and<UserProfileView>components in@clerk/expo.iOS, OAuth from forgot password screen
The inline
AuthViewwas embedded as a childUIHostingControllerinside React Native's view hierarchy. This disruptedASWebAuthenticationSessioncallbacks during OAuth flows started from the forgot password screen, causing the SSO sign-in to silently fail.ClerkAuthNativeViewnow presents itsUIHostingControllerviaUIViewController.present()instead of embedding it as a child view. The visual appearance is unchanged (fullscreen, no animation) but the OAuth callback chain now completes correctly.Also includes:
presentWhenReady()usesUIViewController.transitionCoordinatorto wait for any in-flight modal dismissal (for example,UserProfileViewsign out) instead of a fixed delay, fixing a visible white flash on initial mount.viewDidDisappearchecks the new session before returning acancelledresult so successful auth still propagates.Android, sign-out state cleanup
Three related fixes to prevent the
AuthViewfrom getting stuck on the "Get help / Email support" screen after sign out:Detect new sign-ins by session ID change instead of "null to value".
ClerkAuthExpoView.initialSessionIdis captured at construction, but the view can be instantiated beforesignOuthas finished clearing local state, causing it to capture a stale session ID. Switching to ID inequality lets subsequent sign-ins fire the auth completed event correctly.Per view
ViewModelStoreforClerkAuthExpoView. The clerk-androidAuthView's navigationViewModelwas scoped to theMainActivity, so its navigation state (for example, "Get help" destination) persisted across mount and unmount cycles within the same activity. EachClerkAuthExpoViewinstance now provides its ownViewModelStoreOwnerso theAuthViewgets a freshViewModelscope per mount.Refresh client from server after sign out.
Clerk.auth.signOut()only clears the active session, not the in-progressClerk.client.signIn. After sign out (whether via the JS bridgesignOut(), the inlineUserProfileView, or the modalUserProfileActivity), callClient.getSkippingClientId()to fetch a brand new client. The skipping variant is required becauseClient.get()echoes back the sameclient_idheader, returning the same client with the stalesignInstill attached.Files changed
packages/expo/ios/ClerkExpoModule.swift,ClerkAuthNativeViewmodal presentation andpresentWhenReadypackages/expo/ios/ClerkViewFactory.swift,viewDidDisappearsession detectionpackages/expo/android/.../ClerkAuthExpoView.kt, session ID change detection and per viewViewModelStorepackages/expo/android/.../ClerkExpoModule.kt,Client.getSkippingClientId()aftersignOut()packages/expo/android/.../ClerkUserProfileActivity.kt,Client.getSkippingClientId()afterUserProfilesign outpackages/expo/android/.../ClerkUserProfileExpoView.kt,Client.getSkippingClientId()after inlineUserProfilesign outHow to test
iOS:
NativeComponentQuickstartapp on the iOS simulator.UserProfileView, sign out from there, expect a clean return to the sign in screen.presentAuth()andpresentUserProfile()flows still work.Android:
NativeComponentQuickstartapp on an Android emulator.UserProfileView, sign out from there, then sign back in. Verify the user is not stuck on the "Get help" screen.Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change