diff --git a/src/App.web.tsx b/src/App.web.tsx index cd451e38c..518afd143 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -41,6 +41,7 @@ import { useSessionApi, } from '#/state/session' import {getWebOAuthClient} from '#/state/session/oauth-web-client' +import {consumeOAuthReturnUrl} from '#/state/session/oauth-web-return-url' import {readLastActiveAccount} from '#/state/session/util' import {Provider as ShellStateProvider} from '#/state/shell' import {Provider as ComposerProvider} from '#/state/shell/composer' @@ -72,11 +73,11 @@ import { features, setupDeviceId, } from '#/analytics' -import {SettingsSyncGate} from '#/features/settingsSync' import { prefetchLiveEvents, Provider as LiveEventsProvider, } from '#/features/liveEvents/context' +import {SettingsSyncGate} from '#/features/settingsSync' import * as Geo from '#/geolocation' import {Splash} from '#/Splash' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' @@ -144,6 +145,13 @@ function InnerApp() { }, 'LoginForm', ) + + const returnUrl = consumeOAuthReturnUrl() + if (returnUrl) { + window.location.replace(returnUrl) + return + } + // Clear hash fragment after processing window.history.replaceState(null, '', window.location.pathname) return @@ -187,55 +195,55 @@ function InnerApp() { - - - - - - {/* LabelDefsProvider MUST come before ModerationOptsProvider */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + {/* LabelDefsProvider MUST come before ModerationOptsProvider */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/routes/web.ts b/src/lib/routes/web.ts new file mode 100644 index 000000000..a7b212ccd --- /dev/null +++ b/src/lib/routes/web.ts @@ -0,0 +1,4 @@ +export function replaceWebLocation(href: string) { + if (typeof window === 'undefined') return + window.location.replace(href) +} diff --git a/src/screens/Login/AuthCallback.tsx b/src/screens/Login/AuthCallback.tsx index 0de2a37bc..a16730d87 100644 --- a/src/screens/Login/AuthCallback.tsx +++ b/src/screens/Login/AuthCallback.tsx @@ -2,16 +2,18 @@ import {useEffect} from 'react' import {useNavigation} from '@react-navigation/native' import {type NavigationProp} from '#/lib/routes/types' +import {replaceWebLocation} from '#/lib/routes/web' import {logger} from '#/logger' import {useSessionApi} from '#/state/session' import {getWebOAuthClient} from '#/state/session/oauth-web-client' +import {consumeOAuthReturnUrl} from '#/state/session/oauth-web-return-url' export function AuthCallback() { const {login} = useSessionApi() const navigation = useNavigation() useEffect(() => { - ;(async () => { + void (async () => { try { const client = getWebOAuthClient() const result = await client.init() @@ -26,9 +28,18 @@ export function AuthCallback() { 'LoginForm', ) } + + const returnUrl = consumeOAuthReturnUrl() + if (returnUrl) { + replaceWebLocation(returnUrl) + return + } + navigation.replace('Home') - } catch (e: any) { - logger.error('OAuth callback failed', {error: e.message}) + } catch (e: unknown) { + logger.error('OAuth callback failed', { + error: e instanceof Error ? e.message : String(e), + }) navigation.replace('Home') } })() diff --git a/src/screens/Login/LoginForm.web.tsx b/src/screens/Login/LoginForm.web.tsx index c43da6a9b..dad5b6a4c 100644 --- a/src/screens/Login/LoginForm.web.tsx +++ b/src/screens/Login/LoginForm.web.tsx @@ -21,6 +21,7 @@ import {logger} from '#/logger' import {useSetHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs' import {useSessionApi} from '#/state/session' import {getWebOAuthClient} from '#/state/session/oauth-web-client' +import {saveOAuthReturnUrl} from '#/state/session/oauth-web-return-url' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' @@ -204,6 +205,7 @@ function OAuthLoginFields({ setIsProcessing(true) try { + saveOAuthReturnUrl() const client = getWebOAuthClient() await client.signIn(identifier) // Browser will redirect to authorization server diff --git a/src/state/session/oauth-web-return-url.ts b/src/state/session/oauth-web-return-url.ts new file mode 100644 index 000000000..d001d0ad8 --- /dev/null +++ b/src/state/session/oauth-web-return-url.ts @@ -0,0 +1,25 @@ +const OAUTH_RETURN_URL_KEY = 'oauth_return_url' + +export function saveOAuthReturnUrl(url = window.location.href) { + if (typeof window === 'undefined') return + + window.sessionStorage.setItem(OAUTH_RETURN_URL_KEY, url) +} + +export function consumeOAuthReturnUrl() { + if (typeof window === 'undefined') return undefined + + const savedUrl = window.sessionStorage.getItem(OAUTH_RETURN_URL_KEY) + window.sessionStorage.removeItem(OAUTH_RETURN_URL_KEY) + + if (!savedUrl) return undefined + + try { + const url = new URL(savedUrl, window.location.origin) + if (url.origin !== window.location.origin) return undefined + if (url.pathname === '/auth/web/callback') return undefined + return `${url.pathname}${url.search}${url.hash}` + } catch { + return undefined + } +}