diff --git a/src/CONFIG.ts b/src/CONFIG.ts
index c02ed8065836..8b1dab5b3d71 100644
--- a/src/CONFIG.ts
+++ b/src/CONFIG.ts
@@ -64,6 +64,7 @@ export default {
CONCIERGE_URL_PATHNAME: 'concierge/',
DEVPORTAL_URL_PATHNAME: '_devportal/',
CONCIERGE_URL: `${expensifyURL}concierge/`,
+ SAML_URL: `${expensifyURL}authentication/saml/login`,
},
IS_IN_PRODUCTION: Platform.OS === 'web' ? process.env.NODE_ENV === 'production' : !__DEV__,
IS_IN_STAGING: ENVIRONMENT === CONST.ENVIRONMENT.STAGING,
diff --git a/src/CONST.ts b/src/CONST.ts
index a4f28a9c98c7..0591aa2415c3 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -243,6 +243,7 @@ const CONST = {
CUSTOM_STATUS: 'customStatus',
NEW_DOT_CATEGORIES: 'newDotCategories',
NEW_DOT_TAGS: 'newDotTags',
+ NEW_DOT_SAML: 'newDotSAML',
},
BUTTON_STATES: {
DEFAULT: 'default',
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 7127c1483c26..60c526d6d885 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -36,6 +36,8 @@ export default {
APPLE_SIGN_IN: 'sign-in-with-apple',
GOOGLE_SIGN_IN: 'sign-in-with-google',
DESKTOP_SIGN_IN_REDIRECT: 'desktop-signin-redirect',
+ SAML_SIGN_IN: 'sign-in-with-saml',
+
// This is a special validation URL that will take the user to /workspace/new after validation. This is used
// when linking users from e.com in order to share a session in this app.
ENABLE_PAYMENTS: 'enable-payments',
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 69f905e4a7a3..99d5eb6277cd 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -29,6 +29,7 @@ export default {
SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop',
SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop',
DESKTOP_SIGN_IN_REDIRECT: 'DesktopSignInRedirect',
+ SAML_SIGN_IN: 'SAMLSignIn',
VALIDATE_LOGIN: 'ValidateLogin',
// Iframe screens from olddot
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 9d376c73ea62..e8612c605ea0 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -381,6 +381,14 @@ export default {
termsOfService: 'Terms of Service',
privacy: 'Privacy',
},
+ samlSignIn: {
+ welcomeSAMLEnabled: 'Continue logging in with single sign-on:',
+ orContinueWithMagicCode: 'Or optionally, your company allows signing in with a magic code',
+ useSingleSignOn: 'Use single sign-on',
+ useMagicCode: 'Use magic code',
+ launching: 'Launching...',
+ oneMoment: "One moment while we redirect you to your company's single sign-on portal.",
+ },
reportActionCompose: {
addAction: 'Actions',
dropToUpload: 'Drop to upload',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 316cd1eaed21..e9c7d2d34f79 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -372,6 +372,14 @@ export default {
termsOfService: 'Términos de servicio',
privacy: 'Privacidad',
},
+ samlSignIn: {
+ welcomeSAMLEnabled: 'Continua iniciando sesión con el inicio de sesión único:',
+ orContinueWithMagicCode: 'O, opcionalmente, tu empresa te permite iniciar sesión con un código mágico',
+ useSingleSignOn: 'Usar el inicio de sesión único',
+ useMagicCode: 'Usar código mágico',
+ launching: 'Cargando...',
+ oneMoment: 'Un momento mientras te redirigimos al portal de inicio de sesión único de tu empresa.',
+ },
reportActionCompose: {
addAction: 'Acción',
dropToUpload: 'Suelta el archivo aquí para compartirlo',
diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.js b/src/libs/Navigation/AppNavigator/PublicScreens.js
index 7a87530a2d9e..7b0afb787278 100644
--- a/src/libs/Navigation/AppNavigator/PublicScreens.js
+++ b/src/libs/Navigation/AppNavigator/PublicScreens.js
@@ -8,6 +8,7 @@ import defaultScreenOptions from './defaultScreenOptions';
import UnlinkLoginPage from '../../../pages/UnlinkLoginPage';
import AppleSignInDesktopPage from '../../../pages/signin/AppleSignInDesktopPage';
import GoogleSignInDesktopPage from '../../../pages/signin/GoogleSignInDesktopPage';
+import SAMLSignInPage from '../../../pages/signin/SAMLSignInPage';
const RootStack = createStackNavigator();
@@ -44,6 +45,11 @@ function PublicScreens() {
options={defaultScreenOptions}
component={GoogleSignInDesktopPage}
/>
+
);
}
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index fde5fe400c76..9c9c4e227f41 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -15,6 +15,7 @@ export default {
[SCREENS.CONCIERGE]: ROUTES.CONCIERGE,
AppleSignInDesktop: ROUTES.APPLE_SIGN_IN,
GoogleSignInDesktop: ROUTES.GOOGLE_SIGN_IN,
+ SAMLSignIn: ROUTES.SAML_SIGN_IN,
[SCREENS.DESKTOP_SIGN_IN_REDIRECT]: ROUTES.DESKTOP_SIGN_IN_REDIRECT,
[SCREENS.REPORT_ATTACHMENTS]: ROUTES.REPORT_ATTACHMENTS.route,
diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js
index 117a092c3875..3b623a42689d 100644
--- a/src/libs/actions/Session/index.js
+++ b/src/libs/actions/Session/index.js
@@ -316,7 +316,7 @@ function signInWithShortLivedAuthToken(email, authToken) {
// If the user is signing in with a different account from the current app, should not pass the auto-generated login as it may be tied to the old account.
// scene 1: the user is transitioning to newDot from a different account on oldDot.
// scene 2: the user is transitioning to desktop app from a different account on web app.
- const oldPartnerUserID = credentials.login === email ? credentials.autoGeneratedLogin : '';
+ const oldPartnerUserID = credentials.login === email && credentials.autoGeneratedLogin ? credentials.autoGeneratedLogin : '';
API.read('SignInWithShortLivedAuthToken', {authToken, oldPartnerUserID, skipReauthentication: true}, {optimisticData, successData, failureData});
}
@@ -541,6 +541,10 @@ function clearAccountMessages() {
});
}
+function setAccountError(error) {
+ Onyx.merge(ONYXKEYS.ACCOUNT, {errors: ErrorUtils.getMicroSecondOnyxError(error)});
+}
+
// It's necessary to throttle requests to reauthenticate since calling this multiple times will cause Pusher to
// reconnect each time when we only need to reconnect once. This way, if an authToken is expired and we try to
// subscribe to a bunch of channels at once we will only reauthenticate and force reconnect Pusher once.
@@ -807,6 +811,7 @@ export {
unlinkLogin,
clearSignInData,
clearAccountMessages,
+ setAccountError,
authenticatePusher,
reauthenticatePusher,
invalidateCredentials,
diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.js b/src/pages/LogInWithShortLivedAuthTokenPage.js
index 62eff262611d..875cdf7e8072 100644
--- a/src/pages/LogInWithShortLivedAuthTokenPage.js
+++ b/src/pages/LogInWithShortLivedAuthTokenPage.js
@@ -12,8 +12,7 @@ import themeColors from '../styles/themes/default';
import Icon from '../components/Icon';
import * as Expensicons from '../components/Icon/Expensicons';
import * as Illustrations from '../components/Icon/Illustrations';
-import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
-import compose from '../libs/compose';
+import useLocalize from '../hooks/useLocalize';
import TextLink from '../components/TextLink';
import ONYXKEYS from '../ONYXKEYS';
@@ -33,8 +32,6 @@ const propTypes = {
}),
}).isRequired,
- ...withLocalizePropTypes,
-
/** The details about the account that the user is signing in with */
account: PropTypes.shape({
/** Whether a sign is loading */
@@ -49,15 +46,26 @@ const defaultProps = {
};
function LogInWithShortLivedAuthTokenPage(props) {
+ const {translate} = useLocalize();
+
useEffect(() => {
const email = lodashGet(props, 'route.params.email', '');
// We have to check for both shortLivedAuthToken and shortLivedToken, as the old mobile app uses shortLivedToken, and is not being actively updated.
const shortLivedAuthToken = lodashGet(props, 'route.params.shortLivedAuthToken', '') || lodashGet(props, 'route.params.shortLivedToken', '');
- if (shortLivedAuthToken) {
+
+ // Try to authenticate using the shortLivedToken if we're not already trying to load the accounts
+ if (shortLivedAuthToken && !props.account.isLoading) {
Session.signInWithShortLivedAuthToken(email, shortLivedAuthToken);
return;
}
+
+ // If an error is returned as part of the route, ensure we set it in the onyxData for the account
+ const error = lodashGet(props, 'route.params.error', '');
+ if (error) {
+ Session.setAccountError(error);
+ }
+
const exitTo = lodashGet(props, 'route.params.exitTo', '');
if (exitTo) {
Navigation.isNavigationReady().then(() => {
@@ -82,10 +90,18 @@ function LogInWithShortLivedAuthTokenPage(props) {
src={Illustrations.RocketBlue}
/>
- {props.translate('deeplinkWrapper.launching')}
+ {translate('deeplinkWrapper.launching')}
- {props.translate('deeplinkWrapper.expired')} Navigation.navigate()}>{props.translate('deeplinkWrapper.signIn')}
+ {translate('deeplinkWrapper.expired')}{' '}
+ {
+ Session.clearSignInData();
+ Navigation.navigate();
+ }}
+ >
+ {translate('deeplinkWrapper.signIn')}
+
@@ -105,9 +121,7 @@ LogInWithShortLivedAuthTokenPage.propTypes = propTypes;
LogInWithShortLivedAuthTokenPage.defaultProps = defaultProps;
LogInWithShortLivedAuthTokenPage.displayName = 'LogInWithShortLivedAuthTokenPage';
-export default compose(
- withLocalize,
- withOnyx({
- account: {key: ONYXKEYS.ACCOUNT},
- }),
-)(LogInWithShortLivedAuthTokenPage);
+export default withOnyx({
+ account: {key: ONYXKEYS.ACCOUNT},
+ session: {key: ONYXKEYS.SESSION},
+})(LogInWithShortLivedAuthTokenPage);
diff --git a/src/pages/signin/ChooseSSOOrMagicCode.js b/src/pages/signin/ChooseSSOOrMagicCode.js
new file mode 100644
index 000000000000..32f0776cdbc9
--- /dev/null
+++ b/src/pages/signin/ChooseSSOOrMagicCode.js
@@ -0,0 +1,108 @@
+import React from 'react';
+import {View} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import PropTypes from 'prop-types';
+import _ from 'underscore';
+import styles from '../../styles/styles';
+import ONYXKEYS from '../../ONYXKEYS';
+import Text from '../../components/Text';
+import Button from '../../components/Button';
+import * as Session from '../../libs/actions/Session';
+import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink';
+import Terms from './Terms';
+import CONST from '../../CONST';
+import ROUTES from '../../ROUTES';
+import Navigation from '../../libs/Navigation/Navigation';
+import * as ErrorUtils from '../../libs/ErrorUtils';
+import useLocalize from '../../hooks/useLocalize';
+import useNetwork from '../../hooks/useNetwork';
+import useWindowDimensions from '../../hooks/useWindowDimensions';
+import FormHelpMessage from '../../components/FormHelpMessage';
+
+const propTypes = {
+ /* Onyx Props */
+
+ /** The credentials of the logged in person */
+ credentials: PropTypes.shape({
+ /** The email/phone the user logged in with */
+ login: PropTypes.string,
+ }),
+
+ /** The details about the account that the user is signing in with */
+ account: PropTypes.shape({
+ /** Whether or not a sign on form is loading (being submitted) */
+ isLoading: PropTypes.bool,
+
+ /** Form that is being loaded */
+ loadingForm: PropTypes.oneOf(_.values(CONST.FORMS)),
+
+ /** Whether this account has 2FA enabled or not */
+ requiresTwoFactorAuth: PropTypes.bool,
+
+ /** Server-side errors in the submitted authentication code */
+ errors: PropTypes.objectOf(PropTypes.string),
+ }),
+
+ /** Function that returns whether the user is using SAML or magic codes to log in */
+ setIsUsingMagicCode: PropTypes.func.isRequired,
+};
+
+const defaultProps = {
+ credentials: {},
+ account: {},
+};
+
+function ChooseSSOOrMagicCode({credentials, account, setIsUsingMagicCode}) {
+ const {translate} = useLocalize();
+ const {isOffline} = useNetwork();
+ const {isSmallScreenWidth} = useWindowDimensions();
+
+ return (
+ <>
+
+ {translate('samlSignIn.welcomeSAMLEnabled')}
+
+
+
+
+ >
+ );
+}
+
+ChooseSSOOrMagicCode.propTypes = propTypes;
+ChooseSSOOrMagicCode.defaultProps = defaultProps;
+ChooseSSOOrMagicCode.displayName = 'ChooseSSOOrMagicCode';
+
+export default withOnyx({
+ credentials: {key: ONYXKEYS.CREDENTIALS},
+ account: {key: ONYXKEYS.ACCOUNT},
+})(ChooseSSOOrMagicCode);
diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js
index 2c65b5ff5d37..3576f92be31f 100644
--- a/src/pages/signin/LoginForm/BaseLoginForm.js
+++ b/src/pages/signin/LoginForm/BaseLoginForm.js
@@ -163,7 +163,7 @@ function LoginForm(props) {
useEffect(() => {
// Just call clearAccountMessages on the login page (home route), because when the user is in the transition route and not yet authenticated,
// this component will also be mounted, resetting account.isLoading will cause the app to briefly display the session expiration page.
- if (props.isFocused) {
+ if (props.isFocused && props.isVisible) {
Session.clearAccountMessages();
}
if (!canFocusInputOnScreenFocus() || !input.current || !props.isVisible) {
diff --git a/src/pages/signin/SAMLSignInPage/index.js b/src/pages/signin/SAMLSignInPage/index.js
new file mode 100644
index 000000000000..23ce9b93b8cc
--- /dev/null
+++ b/src/pages/signin/SAMLSignInPage/index.js
@@ -0,0 +1,66 @@
+import React, {useEffect} from 'react';
+import {withOnyx} from 'react-native-onyx';
+import {View} from 'react-native';
+import PropTypes from 'prop-types';
+import ONYXKEYS from '../../../ONYXKEYS';
+import CONFIG from '../../../CONFIG';
+import Icon from '../../../components/Icon';
+import Text from '../../../components/Text';
+import * as Expensicons from '../../../components/Icon/Expensicons';
+import * as Illustrations from '../../../components/Icon/Illustrations';
+import styles from '../../../styles/styles';
+import themeColors from '../../../styles/themes/default';
+import useLocalize from '../../../hooks/useLocalize';
+
+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 {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')}
+
+
+
+
+
+
+ );
+}
+
+SAMLSignInPage.propTypes = propTypes;
+SAMLSignInPage.defaultProps = defaultProps;
+
+export default withOnyx({
+ credentials: {key: ONYXKEYS.CREDENTIALS},
+})(SAMLSignInPage);
diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js
index d5acf8803224..8aae45c279c6 100644
--- a/src/pages/signin/SignInPage.js
+++ b/src/pages/signin/SignInPage.js
@@ -19,7 +19,13 @@ import * as StyleUtils from '../../styles/StyleUtils';
import useLocalize from '../../hooks/useLocalize';
import useWindowDimensions from '../../hooks/useWindowDimensions';
import Log from '../../libs/Log';
+import getPlatform from '../../libs/getPlatform';
+import CONST from '../../CONST';
+import Navigation from '../../libs/Navigation/Navigation';
+import ROUTES from '../../ROUTES';
+import ChooseSSOOrMagicCode from './ChooseSSOOrMagicCode';
import * as ActiveClientManager from '../../libs/ActiveClientManager';
+import * as Session from '../../libs/actions/Session';
const propTypes = {
/** The details about the account that the user is signing in with */
@@ -38,6 +44,18 @@ const propTypes = {
/** Is this account having trouble receiving emails */
hasEmailDeliveryFailure: PropTypes.bool,
+
+ /** Whether or not a sign on form is loading (being submitted) */
+ isLoading: PropTypes.bool,
+
+ /** Form that is being loaded */
+ loadingForm: PropTypes.oneOf(_.values(CONST.FORMS)),
+
+ /** Whether or not the user has SAML enabled on their account */
+ isSAMLEnabled: PropTypes.bool,
+
+ /** Whether or not SAML is required on the account */
+ isSAMLRequired: PropTypes.bool,
}),
/** The credentials of the person signing in */
@@ -64,23 +82,50 @@ const defaultProps = {
/**
* @param {Boolean} hasLogin
* @param {Boolean} hasValidateCode
+ * @param {Object} account
* @param {Boolean} isPrimaryLogin
- * @param {Boolean} isAccountValidated
+ * @param {Boolean} isUsingMagicCode
+ * @param {Boolean} hasInitiatedSAMLLogin
* @param {Boolean} hasEmailDeliveryFailure
* @returns {Object}
*/
-function getRenderOptions({hasLogin, hasValidateCode, hasAccount, isPrimaryLogin, isAccountValidated, hasEmailDeliveryFailure, isClientTheLeader}) {
+function getRenderOptions({hasLogin, hasValidateCode, account, isPrimaryLogin, isUsingMagicCode, hasInitiatedSAMLLogin, isClientTheLeader}) {
+ const hasAccount = !_.isEmpty(account);
+ const isSAMLEnabled = Boolean(account.isSAMLEnabled);
+ 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;
+ }
+
+ // 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
+ // SSO provider's login page
+ if (hasLogin && isSAMLRequired && !shouldInitiateSAMLLogin && !hasInitiatedSAMLLogin && !account.isLoading) {
+ Session.clearSignInData();
+ }
+
const shouldShowLoginForm = isClientTheLeader && !hasLogin && !hasValidateCode;
- const shouldShowEmailDeliveryFailurePage = hasLogin && hasEmailDeliveryFailure;
- const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !isAccountValidated && !hasEmailDeliveryFailure;
- const shouldShowValidateCodeForm = hasAccount && (hasLogin || hasValidateCode) && !isUnvalidatedSecondaryLogin && !hasEmailDeliveryFailure;
- const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowValidateCodeForm || isUnvalidatedSecondaryLogin;
- const shouldShowWelcomeText = shouldShowLoginForm || shouldShowValidateCodeForm || !isClientTheLeader;
+ const shouldShowEmailDeliveryFailurePage = hasLogin && hasEmailDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !shouldInitiateSAMLLogin;
+ const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !account.validated && !hasEmailDeliveryFailure;
+ const shouldShowValidateCodeForm =
+ hasAccount && (hasLogin || hasValidateCode) && !isUnvalidatedSecondaryLogin && !hasEmailDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !isSAMLRequired;
+ const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || isUnvalidatedSecondaryLogin;
+ const shouldShowWelcomeText = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || !isClientTheLeader;
return {
shouldShowLoginForm,
shouldShowEmailDeliveryFailurePage,
shouldShowUnlinkLoginForm: isUnvalidatedSecondaryLogin,
shouldShowValidateCodeForm,
+ shouldShowChooseSSOOrMagicCode,
+ shouldInitiateSAMLLogin,
shouldShowWelcomeHeader,
shouldShowWelcomeText,
};
@@ -96,24 +141,44 @@ function SignInPage({credentials, account, isInModal, activeClients}) {
* and we need it here since welcome text(`welcomeText`) also depends on it */
const [isUsingRecoveryCode, setIsUsingRecoveryCode] = useState(false);
+ /** This state is needed to keep track of whether the user has opted to use magic codes
+ * instead of signing in via SAML when SAML is enabled and not required */
+ const [isUsingMagicCode, setIsUsingMagicCode] = useState(false);
+
+ /** This state is needed to keep track of whether the user has been directed to their SSO provider's login page and
+ * if we need to clear their sign in details so they can enter a login */
+ const [hasInitiatedSAMLLogin, setHasInitiatedSAMLLogin] = useState(false);
+
+ const isClientTheLeader = activeClients && ActiveClientManager.isClientTheLeader();
+
useEffect(() => Performance.measureTTI(), []);
useEffect(() => {
App.setLocale(Localize.getDevicePreferredLocale());
}, []);
- const isClientTheLeader = activeClients && ActiveClientManager.isClientTheLeader();
+ const {
+ shouldShowLoginForm,
+ shouldShowEmailDeliveryFailurePage,
+ shouldShowUnlinkLoginForm,
+ shouldShowValidateCodeForm,
+ shouldShowChooseSSOOrMagicCode,
+ shouldInitiateSAMLLogin,
+ shouldShowWelcomeHeader,
+ shouldShowWelcomeText,
+ } = getRenderOptions({
+ hasLogin: Boolean(credentials.login),
+ hasValidateCode: Boolean(credentials.validateCode),
+ account,
+ isPrimaryLogin: !account.primaryLogin || account.primaryLogin === credentials.login,
+ isUsingMagicCode,
+ hasInitiatedSAMLLogin,
+ isClientTheLeader,
+ });
- const {shouldShowLoginForm, shouldShowEmailDeliveryFailurePage, shouldShowUnlinkLoginForm, shouldShowValidateCodeForm, shouldShowWelcomeHeader, shouldShowWelcomeText} = getRenderOptions(
- {
- hasLogin: Boolean(credentials.login),
- hasValidateCode: Boolean(credentials.validateCode),
- hasAccount: !_.isEmpty(account),
- isPrimaryLogin: !account.primaryLogin || account.primaryLogin === credentials.login,
- isAccountValidated: Boolean(account.validated),
- hasEmailDeliveryFailure: Boolean(account.hasEmailDeliveryFailure),
- isClientTheLeader,
- },
- );
+ if (shouldInitiateSAMLLogin) {
+ setHasInitiatedSAMLLogin(true);
+ Navigation.isNavigationReady().then(() => Navigation.navigate(ROUTES.SAML_SIGN_IN));
+ }
let welcomeHeader = '';
let welcomeText = '';
@@ -147,14 +212,14 @@ function SignInPage({credentials, account, isInModal, activeClients}) {
: translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay});
}
}
- } else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage) {
+ } else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
welcomeHeader = shouldShowSmallScreen ? headerText : translate('welcomeText.welcomeBack');
// Don't show any welcome text if we're showing the user the email delivery failed view
- if (shouldShowEmailDeliveryFailurePage) {
+ if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
welcomeText = '';
}
- } else {
+ } else if (!shouldInitiateSAMLLogin) {
Log.warn('SignInPage in unexpected state!');
}
@@ -182,9 +247,11 @@ function SignInPage({credentials, account, isInModal, activeClients}) {
)}
{shouldShowUnlinkLoginForm && }
+ {shouldShowChooseSSOOrMagicCode && }
{shouldShowEmailDeliveryFailurePage && }
diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js
index 335df7be3188..dc100fffe4f1 100755
--- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js
+++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js
@@ -38,6 +38,12 @@ const propTypes = {
/** Whether or not a sign on form is loading (being submitted) */
isLoading: PropTypes.bool,
+
+ /** Whether or not the user has SAML enabled on their account */
+ isSAMLEnabled: PropTypes.bool,
+
+ /** Whether or not SAML is required on the account */
+ isSAMLRequired: PropTypes.bool,
}),
/** The credentials of the person signing in */
@@ -64,6 +70,9 @@ 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,
};
@@ -199,6 +208,8 @@ 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();
};