From a0dcf260c7ebf060fc79fbe813ce147ca75eb007 Mon Sep 17 00:00:00 2001 From: rory Date: Thu, 18 Jan 2024 16:45:49 -0800 Subject: [PATCH 1/4] Migrate LogOutPreviousUserPage to TS --- src/libs/Navigation/types.ts | 2 +- src/libs/SessionUtils.ts | 4 +- src/pages/LogOutPreviousUserPage.tsx | 60 ++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/pages/LogOutPreviousUserPage.tsx diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 8d227fa6f697..0f2d717399c5 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -409,7 +409,7 @@ type AuthScreensParamList = { shouldForceLogin: string; email: string; shortLivedAuthToken: string; - exitTo: string; + exitTo: Routes; }; [SCREENS.CONCIERGE]: undefined; [SCREENS.REPORT_ATTACHMENTS]: { 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.tsx b/src/pages/LogOutPreviousUserPage.tsx new file mode 100644 index 000000000000..8d2d1a5f9b1d --- /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/lib/types'; +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); From 1a58bfb1826f89452ae1b127d40d003e8a82a60b Mon Sep 17 00:00:00 2001 From: rory Date: Thu, 18 Jan 2024 17:23:36 -0800 Subject: [PATCH 2/4] Remove unnecessary change --- src/libs/Navigation/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 0f2d717399c5..8d227fa6f697 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -409,7 +409,7 @@ type AuthScreensParamList = { shouldForceLogin: string; email: string; shortLivedAuthToken: string; - exitTo: Routes; + exitTo: string; }; [SCREENS.CONCIERGE]: undefined; [SCREENS.REPORT_ATTACHMENTS]: { From 56103e700ed4d52e78dbbc487448f6e797ccac1f Mon Sep 17 00:00:00 2001 From: rory Date: Thu, 8 Feb 2024 23:38:33 -0800 Subject: [PATCH 3/4] Default email and shortLivedAuthToken to empty string --- src/pages/LogOutPreviousUserPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/LogOutPreviousUserPage.tsx b/src/pages/LogOutPreviousUserPage.tsx index 8d2d1a5f9b1d..f68344604dfa 100644 --- a/src/pages/LogOutPreviousUserPage.tsx +++ b/src/pages/LogOutPreviousUserPage.tsx @@ -2,7 +2,7 @@ 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/lib/types'; +import type {OnyxEntry} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import * as SessionUtils from '@libs/SessionUtils'; import type {AuthScreensParamList} from '@navigation/types'; @@ -38,8 +38,8 @@ function LogOutPreviousUserPage({session, route}: LogOutPreviousUserPageProps) { // 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; + const email = route.params.email ?? ''; + const shortLivedAuthToken = route.params.shortLivedAuthToken ?? ''; SessionActions.signInWithShortLivedAuthToken(email, shortLivedAuthToken); } }); From 21711bc0ed1e9dd9e8e0042b539a85e91649d9a1 Mon Sep 17 00:00:00 2001 From: rory Date: Thu, 8 Feb 2024 23:40:24 -0800 Subject: [PATCH 4/4] Delete js page --- src/pages/LogOutPreviousUserPage.js | 96 ----------------------------- 1 file changed, 96 deletions(-) delete mode 100644 src/pages/LogOutPreviousUserPage.js 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);