diff --git a/src/libs/SessionUtils.ts b/src/libs/SessionUtils.ts index c73513c747af..52521d5146cc 100644 --- a/src/libs/SessionUtils.ts +++ b/src/libs/SessionUtils.ts @@ -4,7 +4,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; /** * Determine if the transitioning user is logging in as a new user. */ -function isLoggingInAsNewUser(transitionURL: string, sessionEmail: string): boolean { +function isLoggingInAsNewUser(transitionURL?: string, sessionEmail?: string): boolean { // The OldDot mobile app does not URL encode the parameters, but OldDot web // does. We don't want to deploy OldDot mobile again, so as a work around we // compare the session email to both the decoded and raw email from the transition link. @@ -20,7 +20,7 @@ function isLoggingInAsNewUser(transitionURL: string, sessionEmail: string): bool // If they do not match it might be due to encoding, so check the raw value // Capture the un-encoded text in the email param const emailParamRegex = /[?&]email=([^&]*)/g; - const matches = emailParamRegex.exec(transitionURL); + const matches = emailParamRegex.exec(transitionURL ?? ''); const linkedEmail = matches?.[1] ?? null; return linkedEmail !== sessionEmail; } diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js deleted file mode 100644 index 0d1bf4f47025..000000000000 --- a/src/pages/LogOutPreviousUserPage.js +++ /dev/null @@ -1,96 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useContext, useEffect} from 'react'; -import {Linking, NativeModules} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import InitialUrlContext from '@libs/InitialUrlContext'; -import Log from '@libs/Log'; -import Navigation from '@libs/Navigation/Navigation'; -import * as SessionUtils from '@libs/SessionUtils'; -import * as Session from '@userActions/Session'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; - -const propTypes = { - /** The details about the account that the user is signing in with */ - account: PropTypes.shape({ - /** Whether the account data is loading */ - isLoading: PropTypes.bool, - }), - - /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ - session: PropTypes.shape({ - /** The user's email for the current session */ - email: PropTypes.string, - }), -}; - -const defaultProps = { - account: { - isLoading: false, - }, - session: { - email: null, - }, -}; - -// This page is responsible for handling transitions from OldDot. Specifically, it logs the current user -// out if the transition is for another user. -// -// This component should not do any other navigation as that handled in App.setUpPoliciesAndNavigate -function LogOutPreviousUserPage(props) { - const initUrl = useContext(InitialUrlContext); - useEffect(() => { - Linking.getInitialURL().then((url) => { - const sessionEmail = props.session.email; - const transitionUrl = NativeModules.HybridAppModule ? CONST.DEEPLINK_BASE_URL + initUrl : url; - const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionUrl, sessionEmail); - - if (isLoggingInAsNewUser) { - Session.signOutAndRedirectToSignIn(); - } - - // We need to signin and fetch a new authToken, if a user was already authenticated in NewDot, and was redirected to OldDot - // and their authToken stored in Onyx becomes invalid. - // This workflow is triggered while setting up VBBA. User is redirected from NewDot to OldDot to set up 2FA, and then redirected back to NewDot - // On Enabling 2FA, authToken stored in Onyx becomes expired and hence we need to fetch new authToken - const shouldForceLogin = lodashGet(props, 'route.params.shouldForceLogin', '') === 'true'; - if (shouldForceLogin) { - Log.info('LogOutPreviousUserPage - forcing login with shortLivedAuthToken'); - const email = lodashGet(props, 'route.params.email', ''); - const shortLivedAuthToken = lodashGet(props, 'route.params.shortLivedAuthToken', ''); - Session.signInWithShortLivedAuthToken(email, shortLivedAuthToken); - } - - const exitTo = lodashGet(props, 'route.params.exitTo', ''); - // We don't want to navigate to the exitTo route when creating a new workspace from a deep link, - // because we already handle creating the optimistic policy and navigating to it in App.setUpPoliciesAndNavigate, - // which is already called when AuthScreens mounts. - if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { - Navigation.isNavigationReady().then(() => { - // remove this screen and navigate to exit route - const exitUrl = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo; - Navigation.goBack(); - Navigation.navigate(exitUrl); - }); - } - }); - }, [initUrl, props]); - - return ; -} - -LogOutPreviousUserPage.propTypes = propTypes; -LogOutPreviousUserPage.defaultProps = defaultProps; -LogOutPreviousUserPage.displayName = 'LogOutPreviousUserPage'; - -export default withOnyx({ - account: { - key: ONYXKEYS.ACCOUNT, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(LogOutPreviousUserPage); diff --git a/src/pages/LogOutPreviousUserPage.tsx b/src/pages/LogOutPreviousUserPage.tsx new file mode 100644 index 000000000000..f68344604dfa --- /dev/null +++ b/src/pages/LogOutPreviousUserPage.tsx @@ -0,0 +1,60 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useEffect} from 'react'; +import {Linking} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import * as SessionUtils from '@libs/SessionUtils'; +import type {AuthScreensParamList} from '@navigation/types'; +import * as SessionActions from '@userActions/Session'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; +import type {Session} from '@src/types/onyx'; + +type LogOutPreviousUserPageOnyxProps = { + /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ + session: OnyxEntry; +}; + +type LogOutPreviousUserPageProps = LogOutPreviousUserPageOnyxProps & StackScreenProps; + +// This page is responsible for handling transitions from OldDot. Specifically, it logs the current user +// out if the transition is for another user. +// +// This component should not do any other navigation as that handled in App.setUpPoliciesAndNavigate +function LogOutPreviousUserPage({session, route}: LogOutPreviousUserPageProps) { + useEffect(() => { + Linking.getInitialURL().then((transitionURL) => { + const sessionEmail = session?.email; + const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionURL ?? undefined, sessionEmail); + + if (isLoggingInAsNewUser) { + SessionActions.signOutAndRedirectToSignIn(); + } + + // We need to signin and fetch a new authToken, if a user was already authenticated in NewDot, and was redirected to OldDot + // and their authToken stored in Onyx becomes invalid. + // This workflow is triggered while setting up VBBA. User is redirected from NewDot to OldDot to set up 2FA, and then redirected back to NewDot + // On Enabling 2FA, authToken stored in Onyx becomes expired and hence we need to fetch new authToken + const shouldForceLogin = route.params.shouldForceLogin === 'true'; + if (shouldForceLogin) { + const email = route.params.email ?? ''; + const shortLivedAuthToken = route.params.shortLivedAuthToken ?? ''; + SessionActions.signInWithShortLivedAuthToken(email, shortLivedAuthToken); + } + }); + + // We only want to run this effect once on mount (when the page first loads after transitioning from OldDot) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ; +} + +LogOutPreviousUserPage.displayName = 'LogOutPreviousUserPage'; + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(LogOutPreviousUserPage);