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..ec2107c0284 100644 --- a/packages/expo/src/hooks/useOAuth.ts +++ b/packages/expo/src/hooks/useOAuth.ts @@ -77,18 +77,20 @@ export function useOAuth(useOAuthParams: UseOAuthFlowParams) { const { externalVerificationRedirectURL } = signIn.firstFactorVerification; - const authSessionResult = await WebBrowserModule.openAuthSessionAsync( - // @ts-ignore - externalVerificationRedirectURL.toString(), - oauthRedirectUrl, - ); - - // @ts-expect-error - const { type, url } = authSessionResult || {}; + let authSessionResult: WebBrowser.WebBrowserAuthSessionResult; + try { + authSessionResult = await WebBrowserModule.openAuthSessionAsync( + // @ts-ignore + externalVerificationRedirectURL.toString(), + oauthRedirectUrl, + ); + } finally { + WebBrowserModule.dismissBrowser(); + } // 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: '', @@ -98,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 }); 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,