diff --git a/src/components/SAMLLoadingIndicator.js b/src/components/SAMLLoadingIndicator.js new file mode 100644 index 000000000000..d00c5a032a72 --- /dev/null +++ b/src/components/SAMLLoadingIndicator.js @@ -0,0 +1,42 @@ +import React from 'react'; +import {StyleSheet, View} from 'react-native'; +import useLocalize from '@hooks/useLocalize'; +import styles from '@styles/styles'; +import themeColors from '@styles/themes/default'; +import Icon from './Icon'; +import * as Expensicons from './Icon/Expensicons'; +import * as Illustrations from './Icon/Illustrations'; +import Text from './Text'; + +function SAMLLoadingIndicator() { + const {translate} = useLocalize(); + return ( + + + + + + {translate('samlSignIn.launching')} + + {translate('samlSignIn.oneMoment')} + + + + + + + ); +} + +SAMLLoadingIndicator.displayName = 'SAMLLoadingIndicator'; + +export default SAMLLoadingIndicator; diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.js b/src/pages/LogInWithShortLivedAuthTokenPage.js index 8534961b3337..16d0c3909d62 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.js +++ b/src/pages/LogInWithShortLivedAuthTokenPage.js @@ -125,5 +125,4 @@ LogInWithShortLivedAuthTokenPage.displayName = 'LogInWithShortLivedAuthTokenPage export default withOnyx({ account: {key: ONYXKEYS.ACCOUNT}, - session: {key: ONYXKEYS.SESSION}, })(LogInWithShortLivedAuthTokenPage); diff --git a/src/pages/signin/SAMLSignInPage/index.js b/src/pages/signin/SAMLSignInPage/index.js index b93e05c7a2b1..ec3cc01197bd 100644 --- a/src/pages/signin/SAMLSignInPage/index.js +++ b/src/pages/signin/SAMLSignInPage/index.js @@ -1,14 +1,7 @@ import PropTypes from 'prop-types'; import React, {useEffect} from 'react'; -import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; -import * as Illustrations from '@components/Icon/Illustrations'; -import Text from '@components/Text'; -import useLocalize from '@hooks/useLocalize'; -import useTheme from '@styles/themes/useTheme'; -import useThemeStyles from '@styles/useThemeStyles'; +import SAMLLoadingIndicator from '@components/SAMLLoadingIndicator'; import CONFIG from '@src/CONFIG'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -25,39 +18,11 @@ const defaultProps = { }; function SAMLSignInPage({credentials}) { - const theme = useTheme(); - const styles = useThemeStyles(); - const {translate} = useLocalize(); - useEffect(() => { window.open(`${CONFIG.EXPENSIFY.SAML_URL}?email=${credentials.login}&referer=${CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER}`, '_self'); }, [credentials.login]); - return ( - - - - - - {translate('samlSignIn.launching')} - - {translate('samlSignIn.oneMoment')} - - - - - - - ); + return ; } SAMLSignInPage.propTypes = propTypes; diff --git a/src/pages/signin/SAMLSignInPage/index.native.js b/src/pages/signin/SAMLSignInPage/index.native.js new file mode 100644 index 000000000000..502e26e337b9 --- /dev/null +++ b/src/pages/signin/SAMLSignInPage/index.native.js @@ -0,0 +1,95 @@ +import PropTypes from 'prop-types'; +import React, {useCallback, useState} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import WebView from 'react-native-webview'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import SAMLLoadingIndicator from '@components/SAMLLoadingIndicator'; +import ScreenWrapper from '@components/ScreenWrapper'; +import getPlatform from '@libs/getPlatform'; +import Navigation from '@libs/Navigation/Navigation'; +import * as Session from '@userActions/Session'; +import CONFIG from '@src/CONFIG'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; + +const propTypes = { + /** The credentials of the logged in person */ + credentials: PropTypes.shape({ + /** The email/phone the user logged in with */ + login: PropTypes.string, + }), +}; + +const defaultProps = { + credentials: {}, +}; + +function SAMLSignInPage({credentials}) { + const samlLoginURL = `${CONFIG.EXPENSIFY.SAML_URL}?email=${credentials.login}&referer=${CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER}&platform=${getPlatform()}`; + const [showNavigation, shouldShowNavigation] = useState(true); + + /** + * Handles in-app navigation once we get a response back from Expensify + * + * @param {String} params.url + */ + const handleNavigationStateChange = useCallback( + ({url}) => { + // If we've gotten a callback then remove the option to navigate back to the sign in page + if (url.includes('loginCallback')) { + shouldShowNavigation(false); + } + + const searchParams = new URLSearchParams(new URL(url).search); + if (searchParams.has('shortLivedAuthToken')) { + const shortLivedAuthToken = searchParams.get('shortLivedAuthToken'); + Session.signInWithShortLivedAuthToken(credentials.login, shortLivedAuthToken); + } + + // If the login attempt is unsuccessful, set the error message for the account and redirect to sign in page + if (searchParams.has('error')) { + Session.clearSignInData(); + Session.setAccountError(searchParams.get('error')); + Navigation.navigate(ROUTES.HOME); + } + }, + [credentials.login, shouldShowNavigation], + ); + + return ( + + {showNavigation && ( + { + Session.clearSignInData(); + Navigation.navigate(ROUTES.HOME); + }} + /> + )} + + } + onNavigationStateChange={handleNavigationStateChange} + /> + + + ); +} + +SAMLSignInPage.propTypes = propTypes; +SAMLSignInPage.defaultProps = defaultProps; +SAMLSignInPage.displayName = 'SAMLSignInPage'; + +export default withOnyx({ + credentials: {key: ONYXKEYS.CREDENTIALS}, +})(SAMLSignInPage); diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index 24b16177e6ff..6676dc99b911 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -10,7 +10,6 @@ import useLocalize from '@hooks/useLocalize'; import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ActiveClientManager from '@libs/ActiveClientManager'; -import getPlatform from '@libs/getPlatform'; import * as Localize from '@libs/Localize'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -103,15 +102,9 @@ function getRenderOptions({hasLogin, hasValidateCode, account, isPrimaryLogin, i const isSAMLRequired = Boolean(account.isSAMLRequired); const hasEmailDeliveryFailure = Boolean(account.hasEmailDeliveryFailure); - // SAML is temporarily restricted to users on the beta or to users signing in on web and mweb - let shouldShowChooseSSOOrMagicCode = false; - let shouldInitiateSAMLLogin = false; - const platform = getPlatform(); - if (platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP) { - // True if the user has SAML required and we haven't already initiated SAML for their account - shouldInitiateSAMLLogin = hasAccount && hasLogin && isSAMLRequired && !hasInitiatedSAMLLogin && account.isLoading; - shouldShowChooseSSOOrMagicCode = hasAccount && hasLogin && isSAMLEnabled && !isSAMLRequired && !isUsingMagicCode; - } + // True if the user has SAML required and we haven't already initiated SAML for their account + const shouldInitiateSAMLLogin = hasAccount && hasLogin && isSAMLRequired && !hasInitiatedSAMLLogin && account.isLoading; + const shouldShowChooseSSOOrMagicCode = hasAccount && hasLogin && isSAMLEnabled && !isSAMLRequired && !isUsingMagicCode; // SAML required users may reload the login page after having already entered their login details, in which // case we want to clear their sign in data so they don't end up in an infinite loop redirecting back to their @@ -167,6 +160,19 @@ function SignInPageInner({credentials, account, isInModal, activeClients, prefer } App.setLocale(Localize.getDevicePreferredLocale()); }, [preferredLocale]); + useEffect(() => { + if (credentials.login) { + return; + } + + // If we don't have a login set, reset the user's SAML login preferences + if (isUsingMagicCode) { + setIsUsingMagicCode(false); + } + if (hasInitiatedSAMLLogin) { + setHasInitiatedSAMLLogin(false); + } + }, [credentials.login, isUsingMagicCode, setIsUsingMagicCode, hasInitiatedSAMLLogin, setHasInitiatedSAMLLogin]); const { shouldShowLoginForm, @@ -231,7 +237,7 @@ function SignInPageInner({credentials, account, isInModal, activeClients, prefer if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) { welcomeText = ''; } - } else if (!shouldInitiateSAMLLogin) { + } else if (!shouldInitiateSAMLLogin && !hasInitiatedSAMLLogin) { Log.warn('SignInPage in unexpected state!'); } @@ -261,7 +267,6 @@ function SignInPageInner({credentials, account, isInModal, activeClients, prefer )} {shouldShowUnlinkLoginForm && } diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index e409836005c4..7c48d557cd16 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -70,9 +70,6 @@ const propTypes = { /** Function to change `isUsingRecoveryCode` state when user toggles between 2fa code and recovery code */ setIsUsingRecoveryCode: PropTypes.func.isRequired, - /** Function to change `isUsingMagicCode` state when the user goes back to the login page */ - setIsUsingMagicCode: PropTypes.func.isRequired, - ...withLocalizePropTypes, }; @@ -210,8 +207,6 @@ function BaseValidateCodeForm(props) { * Clears local and Onyx sign in states */ const clearSignInData = () => { - // Reset the user's preference for signing in with SAML versus magic codes - props.setIsUsingMagicCode(false); clearLocalSignInData(); Session.clearSignInData(); };