Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SAML NewDot] Add SAML flow for web, mweb, desktop #28372

Merged
merged 51 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
7f9875e
add SAMLEnabledForm
NikkiWines Sep 26, 2023
b7f9376
update SignInPage to route to SAMLEnabledForm and SAMLSignInPage
NikkiWines Sep 26, 2023
0551e55
add SAMLSignInPage and routing
NikkiWines Sep 26, 2023
a4b6622
add beta and consts
NikkiWines Sep 26, 2023
e619191
add routing for SAML required flow
NikkiWines Sep 27, 2023
66a46b4
Merge branch 'main' of github.com:Expensify/App into nikki-saml-newdo…
NikkiWines Sep 27, 2023
75e4482
add updated copy for SAML sign in page
NikkiWines Sep 28, 2023
31222f1
redirect back to SignInPage instead of showing ValidateCodePage in su…
NikkiWines Sep 28, 2023
f99cf33
fix login redirecting when SAML enabled user opts to sign in with a m…
NikkiWines Sep 28, 2023
f144da8
add saml spanish translations
NikkiWines Sep 28, 2023
0c74b4a
minor style and props validation
NikkiWines Sep 28, 2023
464f2dd
include login in useEffect dependency array
NikkiWines Sep 28, 2023
bf8f5d4
minor style and linting fixes
NikkiWines Sep 28, 2023
4b08ced
update copy
NikkiWines Sep 29, 2023
4308871
prettier style
NikkiWines Sep 29, 2023
3813e1a
use destructured props and hooks
NikkiWines Oct 2, 2023
e66d4d7
rename file according to conventions
NikkiWines Oct 2, 2023
6c8e9cc
remove useless compose
NikkiWines Oct 2, 2023
5f82eb1
minor cleanup
NikkiWines Oct 2, 2023
a3ab496
prettier
NikkiWines Oct 2, 2023
4ba9b0d
fix state logic for whether or not the user has opted to use magic co…
NikkiWines Oct 3, 2023
960bda8
minor pr comments
NikkiWines Oct 3, 2023
a4dd935
Merge branch 'main' of https://github.com/Expensify/App into nikki-sa…
NikkiWines Oct 3, 2023
9c34b6b
add support for showing error messages when returned in url
NikkiWines Oct 3, 2023
a11f6f6
add desktop support because it works out of the box ????
NikkiWines Oct 3, 2023
e3ffff9
Revert "add desktop support because it works out of the box ????" - i…
NikkiWines Oct 3, 2023
12bf1cc
some edge case handling and saml required redirect logic
NikkiWines Oct 4, 2023
49c03d1
Merge branch 'main' of https://github.com/Expensify/App into nikki-sa…
NikkiWines Oct 4, 2023
eb8468b
don't re-call signInWithShortLivedAUthToken if we're already loading …
NikkiWines Oct 4, 2023
2a86c47
fix props
NikkiWines Oct 4, 2023
ef1d7f9
some linter fixes
NikkiWines Oct 4, 2023
292fa79
minor style
NikkiWines Oct 4, 2023
44ecdaa
Merge branch 'main' of https://github.com/Expensify/App into nikki-sa…
NikkiWines Oct 9, 2023
887960c
fix merge issue
NikkiWines Oct 9, 2023
a5c1152
prettier
NikkiWines Oct 9, 2023
26585bc
fix console log for rendering and ensure users that are SAML required…
NikkiWines Oct 10, 2023
5a27e2b
remove redundant bool cast
NikkiWines Oct 10, 2023
894b588
re-add erroneously removed logic
NikkiWines Oct 10, 2023
a96f0b1
re-add erroneously removed logic
NikkiWines Oct 10, 2023
12c291b
fix import
NikkiWines Oct 10, 2023
d19ce5b
Merge branch 'main' of https://github.com/Expensify/App into nikki-sa…
NikkiWines Oct 10, 2023
0c67e94
fix import...again
NikkiWines Oct 10, 2023
062fb6b
Merge branch 'main' of https://github.com/Expensify/App into nikki-sa…
NikkiWines Oct 11, 2023
df377f2
Revert "Merge branch 'main' of https://github.com/Expensify/App into …
NikkiWines Oct 11, 2023
371372c
Merge branch 'main' of https://github.com/Expensify/App into nikki-sa…
NikkiWines Oct 11, 2023
23aaee1
resolve weird account state when saml required users navigate back to…
NikkiWines Oct 11, 2023
a6b7b6b
use const where appropriate
NikkiWines Oct 11, 2023
4447dc2
update comment
NikkiWines Oct 13, 2023
f6c0d58
Merge branch 'main' of https://github.com/Expensify/App into nikki-sa…
NikkiWines Oct 13, 2023
d7982fa
remove beta, update some old styles
NikkiWines Oct 13, 2023
b793275
remove unused import
NikkiWines Oct 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CONFIG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,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,
Expand Down
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ const CONST = {
CUSTOM_STATUS: 'customStatus',
NEW_DOT_CATEGORIES: 'newDotCategories',
NEW_DOT_TAGS: 'newDotTags',
NEW_DOT_SAML: 'newDotSAML'
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand Down
2 changes: 2 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,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',
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export default {
SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop',
SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop',
DESKTOP_SIGN_IN_REDIRECT: 'DesktopSignInRedirect',
SAML_SIGN_IN: 'SAMLSignIN'
} as const;
8 changes: 8 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ export default {
termsOfService: 'Terms of Service',
privacy: 'Privacy',
},
samlSignIn: {
welcomeSAMLEnabled: 'Continue logging in with your company\'s Single Sign-On:',
orContinueWithMagicCode: 'Or optionally, your company allows signing in with a magic code',
useSingleSignOn: 'Use Single Sign-On',
useMagicCode: 'Use Magic Code',
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
launching: 'Launching...',
oneMoment: 'One moment while we redirect you to your company\'s Single Sign-On portal.',
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
},
reportActionCompose: {
addAction: 'Actions',
dropToUpload: 'Drop to upload',
Expand Down
8 changes: 8 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@ export default {
termsOfService: 'Términos de servicio',
privacy: 'Privacidad',
},
samlSignIn: {
welcomeSAMLEnabled: 'Continua iniciando sesión con el inicio de sesión único de su empresa:',
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
orContinueWithMagicCode: 'O, opcionalmente, tu empresa te permite iniciar sesión con un código mágico',
useSingleSignOn: 'Utilizar 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',
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/AppNavigator/PublicScreens.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -44,6 +45,11 @@ function PublicScreens() {
options={defaultScreenOptions}
component={GoogleSignInDesktopPage}
/>
<RootStack.Screen
name="SAMLSignIn"
options={defaultScreenOptions}
component={SAMLSignInPage}
/>
</RootStack.Navigator>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
5 changes: 5 additions & 0 deletions src/libs/Permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ function canUseLinkPreviews(): boolean {
return false;
}

function canUseSAML(betas: Beta[]): boolean {
return betas?.includes(CONST.BETAS.NEW_DOT_SAML) || canUseAllBetas(betas);
}

export default {
canUseChronos,
canUsePayWithExpensify,
Expand All @@ -77,4 +81,5 @@ export default {
canUseCategories,
canUseTags,
canUseLinkPreviews,
canUseSAML,
};
109 changes: 109 additions & 0 deletions src/pages/signin/SAMLEnabledForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import styles from '../../styles/styles';
import ONYXKEYS from '../../ONYXKEYS';
import compose from '../../libs/compose';
import Text from '../../components/Text';
import Button from '../../components/Button';
import {withNetwork} from '../../components/OnyxProvider';
import networkPropTypes from '../../components/networkPropTypes';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions';
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';

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) */
loading: PropTypes.bool,
}),

/** Information about the network */
network: networkPropTypes.isRequired,

/** Function to change whether the user is using SAML or magic codes to log in */
setIsUsingSAMLLogin: PropTypes.func.isRequired,

...withLocalizePropTypes,

...windowDimensionsPropTypes,
};

const defaultProps = {
credentials: {},
account: {},
};

function SAMLEnabledForm (props) {
return (
<>
<View>
<Text style={[styles.loginHeroBody, styles.mb5, styles.textNormal, !props.isSmallScreenWidth ? styles.textAlignLeft : {}]}>
{props.translate('samlSignIn.welcomeSAMLEnabled')}
</Text>
<Button
isDisabled={props.network.isOffline}
success
style={[styles.mv3]}
text={props.translate('samlSignIn.useSingleSignOn')}
isLoading={props.account.isLoading}
onPress={() => {
Navigation.navigate(ROUTES.SAML_SIGN_IN);
}}
/>

<View style={[styles.mt5]}>
<Text style={[styles.loginHeroBody, styles.mb5, styles.textNormal, !props.isSmallScreenWidth ? styles.textAlignLeft : {}]}>
{props.translate('samlSignIn.orContinueWithMagicCode')}
</Text>
</View>

<Button
isDisabled={props.network.isOffline}
style={[styles.mv3]}
text={props.translate('samlSignIn.useMagicCode')}
isLoading={
props.account.isLoading && props.account.loadingForm === (props.account.requiresTwoFactorAuth ? CONST.FORMS.VALIDATE_TFA_CODE_FORM : CONST.FORMS.VALIDATE_CODE_FORM)
}
onPress={() => {
Session.resendValidateCode(props.credentials.login);
props.setIsUsingSAMLLogin(false);
}}
/>
<ChangeExpensifyLoginLink onPress={() => Session.clearSignInData()} />
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
</View>
<View style={[styles.mt5, styles.signInPageWelcomeTextContainer]}>
<Terms />
</View>
</>
);
}

SAMLEnabledForm.propTypes = propTypes;
SAMLEnabledForm.defaultProps = defaultProps;
SAMLEnabledForm.displayName = 'SAMLEnabledForm';

export default compose(
withLocalize,
withWindowDimensions,
withNetwork(),
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
withOnyx({
credentials: {key: ONYXKEYS.CREDENTIALS},
account: {key: ONYXKEYS.ACCOUNT},
}),
)(SAMLEnabledForm);
69 changes: 69 additions & 0 deletions src/pages/signin/SAMLSignInPage/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, {useEffect} from 'react';
import {withOnyx} from 'react-native-onyx';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import compose from '../../../libs/compose';
import ONYXKEYS from '../../../ONYXKEYS';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
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';

const propTypes = {
/** The credentials of the logged in person */
credentials: PropTypes.shape({
/** The email/phone the user logged in with */
login: PropTypes.string,
}),
...withLocalizePropTypes,
}
const defaultProps = {
credentials: {},
};

function SAMLSignInPage(props) {
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
useEffect(() => {
window.open(`${CONFIG.EXPENSIFY.SAML_URL}?email=${props.credentials.login}&referer=${CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER}`, '_self')

}, [props.credentials.login]);

return (
<View style={styles.deeplinkWrapperContainer}>
<View style={styles.deeplinkWrapperMessage}>
<View style={styles.mb2}>
<Icon
width={200}
height={164}
src={Illustrations.RocketBlue}
/>
</View>
<Text style={[styles.textHeadline, styles.textXXLarge, styles.textAlignCenter]}>{props.translate('samlSignIn.launching')}</Text>
<View style={[styles.mt2, styles.mh2, styles.fontSizeNormal, styles.textAlignCenter]}>
<Text style={[styles.textAlignCenter]}>{props.translate('samlSignIn.oneMoment')}</Text>
</View>
</View>
<View style={styles.deeplinkWrapperFooter}>
<Icon
width={154}
height={34}
fill={themeColors.success}
src={Expensicons.ExpensifyWordmark}
/>
</View>
</View>
);
}

SAMLSignInPage.propTypes = propTypes;
SAMLSignInPage.defaultProps = defaultProps;

export default compose (
withLocalize,
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
withOnyx({
credentials: {key: ONYXKEYS.CREDENTIALS},
}),
)(SAMLSignInPage);
Loading
Loading