From eff72f5d8da63542e4786c700596d0b31cfbe728 Mon Sep 17 00:00:00 2001 From: ChrisCanin Date: Thu, 19 Mar 2026 09:39:57 -0700 Subject: [PATCH 1/2] fix(expo): dismiss browser after SSO/OAuth flow to prevent stuck sessions on re-login --- .changeset/fix-sso-browser-dismissal.md | 5 +++++ packages/expo/src/hooks/useOAuth.ts | 16 ++++++++++------ packages/expo/src/hooks/useSSO.ts | 19 ++++++++++++++----- 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 .changeset/fix-sso-browser-dismissal.md diff --git a/.changeset/fix-sso-browser-dismissal.md b/.changeset/fix-sso-browser-dismissal.md new file mode 100644 index 00000000000..f29d7ed67c8 --- /dev/null +++ b/.changeset/fix-sso-browser-dismissal.md @@ -0,0 +1,5 @@ +--- +'@clerk/expo': patch +--- + +Fix SSO/OAuth browser not being dismissed after authentication completes. On some platforms the in-app browser would remain open in the background after a successful flow, causing subsequent sign-in attempts to fail or appear frozen. `WebBrowser.dismissBrowser()` is now called unconditionally after `openAuthSessionAsync` resolves in both `useSSO` and `useOAuth`. diff --git a/packages/expo/src/hooks/useOAuth.ts b/packages/expo/src/hooks/useOAuth.ts index 80487ecf8ad..2ac5000bec9 100644 --- a/packages/expo/src/hooks/useOAuth.ts +++ b/packages/expo/src/hooks/useOAuth.ts @@ -77,13 +77,17 @@ export function useOAuth(useOAuthParams: UseOAuthFlowParams) { const { externalVerificationRedirectURL } = signIn.firstFactorVerification; - const authSessionResult = await WebBrowserModule.openAuthSessionAsync( - // @ts-ignore - externalVerificationRedirectURL.toString(), - oauthRedirectUrl, - ); + let authSessionResult: WebBrowser.WebBrowserAuthSessionResult; + try { + authSessionResult = await WebBrowserModule.openAuthSessionAsync( + // @ts-ignore + externalVerificationRedirectURL.toString(), + oauthRedirectUrl, + ); + } finally { + WebBrowserModule.dismissBrowser(); + } - // @ts-expect-error const { type, url } = authSessionResult || {}; // TODO: Check all the possible AuthSession results diff --git a/packages/expo/src/hooks/useSSO.ts b/packages/expo/src/hooks/useSSO.ts index 7dff8a208a2..d138324b653 100644 --- a/packages/expo/src/hooks/useSSO.ts +++ b/packages/expo/src/hooks/useSSO.ts @@ -86,11 +86,20 @@ export function useSSO() { return errorThrower.throw('Missing external verification redirect URL for SSO flow'); } - const authSessionResult = await WebBrowserModule.openAuthSessionAsync( - externalVerificationRedirectURL.toString(), - redirectUrl, - authSessionOptions, - ); + let authSessionResult: WebBrowser.WebBrowserAuthSessionResult; + try { + authSessionResult = await WebBrowserModule.openAuthSessionAsync( + externalVerificationRedirectURL.toString(), + redirectUrl, + authSessionOptions, + ); + } finally { + // Ensure the browser is always dismissed after the auth session completes or fails. + // Without this, the browser can remain open in the background on some platforms, + // causing subsequent SSO attempts to fail or the browser to appear frozen. + WebBrowserModule.dismissBrowser(); + } + if (authSessionResult.type !== 'success' || !authSessionResult.url) { return { createdSessionId: null, From a0943451bdb7492c7441fa5fc122e559f752079f Mon Sep 17 00:00:00 2001 From: ChrisCanin Date: Wed, 25 Mar 2026 11:05:14 -0700 Subject: [PATCH 2/2] fix(expo): fix TypeScript error in useOAuth auth session result handling Access authSessionResult.type and authSessionResult.url directly with proper discriminated union narrowing instead of destructuring, matching the pattern used in useSSO.ts. --- packages/expo/src/hooks/useOAuth.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/expo/src/hooks/useOAuth.ts b/packages/expo/src/hooks/useOAuth.ts index 2ac5000bec9..ec2107c0284 100644 --- a/packages/expo/src/hooks/useOAuth.ts +++ b/packages/expo/src/hooks/useOAuth.ts @@ -88,11 +88,9 @@ export function useOAuth(useOAuthParams: UseOAuthFlowParams) { WebBrowserModule.dismissBrowser(); } - const { type, url } = authSessionResult || {}; - // TODO: Check all the possible AuthSession results // https://docs.expo.dev/versions/latest/sdk/auth-session/#returns-7 - if (type !== 'success') { + if (authSessionResult.type !== 'success' || !authSessionResult.url) { return { authSessionResult, createdSessionId: '', @@ -102,7 +100,7 @@ export function useOAuth(useOAuthParams: UseOAuthFlowParams) { }; } - const params = new URL(url).searchParams; + const params = new URL(authSessionResult.url).searchParams; const rotatingTokenNonce = params.get('rotating_token_nonce') || ''; await signIn.reload({ rotatingTokenNonce });