From d8aa50d470e3d1cbeea78b37220f47207d4a0bc0 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 6 Jan 2023 00:08:36 +0530 Subject: [PATCH 1/6] Revert "Revert "Display banner to enable 2FA when setting up VBBA"" --- .../simple-illustration__shield.svg | 77 +++++++++++++++++++ src/components/Icon/Illustrations.js | 2 + src/languages/en.js | 3 + src/languages/es.js | 3 + .../ReimbursementAccount/Enable2FAPrompt.js | 46 +++++++++++ .../ReimbursementAccount/ValidationStep.js | 32 +++++++- 6 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 assets/images/simple-illustrations/simple-illustration__shield.svg create mode 100644 src/pages/ReimbursementAccount/Enable2FAPrompt.js diff --git a/assets/images/simple-illustrations/simple-illustration__shield.svg b/assets/images/simple-illustrations/simple-illustration__shield.svg new file mode 100644 index 0000000000000..5d56b9c3acb20 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__shield.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.js b/src/components/Icon/Illustrations.js index 20c2006a3b82f..ebfd868466926 100644 --- a/src/components/Icon/Illustrations.js +++ b/src/components/Icon/Illustrations.js @@ -17,6 +17,7 @@ import RocketOrange from '../../../assets/images/product-illustrations/rocket--o import TadaYellow from '../../../assets/images/product-illustrations/tada--yellow.svg'; import TadaBlue from '../../../assets/images/product-illustrations/tada--blue.svg'; import GpsTrackOrange from '../../../assets/images/product-illustrations/gps-track--orange.svg'; +import ShieldYellow from '../../../assets/images/simple-illustrations/simple-illustration__shield.svg'; import MoneyReceipts from '../../../assets/images/simple-illustrations/simple-illustration__money-receipts.svg'; import PinkBill from '../../../assets/images/simple-illustrations/simple-illustration__bill.svg'; import CreditCardsNew from '../../../assets/images/simple-illustrations/simple-illustration__credit-cards.svg'; @@ -54,6 +55,7 @@ export { TadaYellow, TadaBlue, GpsTrackOrange, + ShieldYellow, MoneyReceipts, PinkBill, CreditCardsNew, diff --git a/src/languages/en.js b/src/languages/en.js index 08034100f283a..6bd9f50afa0ed 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -803,6 +803,9 @@ export default { letsChatCTA: 'Yes, let\'s chat', letsChatText: 'Thanks for doing that. We need your help verifying a few pieces of information, but we can work this out quickly over chat. Ready?', letsChatTitle: 'Let\'s chat!', + enable2FATitle: 'Prevent fraud, enable two-factor authentication!', + enable2FAText: 'We take your security seriously, so please set up two-factor authentication for your account now. That will allow us to dispute Expensify Card digital transactions, and will reduce your risk for fraud.', + secureYourAccount: 'Secure your account', }, beneficialOwnersStep: { additionalInformation: 'Additional information', diff --git a/src/languages/es.js b/src/languages/es.js index f7ca43b764a6d..7f4e6e3ada2a2 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -805,6 +805,9 @@ export default { letsChatCTA: 'Sí, vamos a chatear', letsChatText: 'Gracias. Necesitamos tu ayuda para verificar la información, pero podemos hacerlo rápidamente a través del chat. ¿Estás listo?', letsChatTitle: '¡Vamos a chatear!', + enable2FATitle: 'Evita fraudes, activa la autenticación de dos factores!', + enable2FAText: 'Tu seguridad es importante para nosotros, por favor configura ahora la autenticación de dos factores. Eso nos permitirá disputar las transacciones de la Tarjeta Expensify y reducirá tu riesgo de fraude.', + secureYourAccount: 'Asegura tu cuenta', }, beneficialOwnersStep: { additionalInformation: 'Información adicional', diff --git a/src/pages/ReimbursementAccount/Enable2FAPrompt.js b/src/pages/ReimbursementAccount/Enable2FAPrompt.js new file mode 100644 index 0000000000000..6b89006bb43d6 --- /dev/null +++ b/src/pages/ReimbursementAccount/Enable2FAPrompt.js @@ -0,0 +1,46 @@ +import React from 'react'; +import {View} from 'react-native'; +import Text from '../../components/Text'; +import styles from '../../styles/styles'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import * as Expensicons from '../../components/Icon/Expensicons'; +import * as Illustrations from '../../components/Icon/Illustrations'; +import Section from '../../components/Section'; +import * as Link from '../../libs/actions/Link'; +import CONFIG from '../../CONFIG'; +import ROUTES from '../../ROUTES'; +import themeColors from '../../styles/themes/default'; + +const propTypes = { + ...withLocalizePropTypes, +}; +const Enable2FAPrompt = props => ( +
{ + Link.openOldDotLink(`settings?param={"section":"account","action":"enableTwoFactorAuth","exitTo":"${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.BANK_ACCOUNT}"}`); + }, + icon: Expensicons.Shield, + shouldShowRightIcon: true, + iconRight: Expensicons.NewWindow, + iconFill: themeColors.success, + wrapperStyle: [styles.cardMenuItem], + }, + ]} + > + + + {props.translate('validationStep.enable2FAText')} + + +
+); + +Enable2FAPrompt.propTypes = propTypes; +Enable2FAPrompt.displayName = 'Enable2FAPrompt'; + +export default withLocalize(Enable2FAPrompt); diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js index 97c3d62d54b06..6279cca7519ca 100644 --- a/src/pages/ReimbursementAccount/ValidationStep.js +++ b/src/pages/ReimbursementAccount/ValidationStep.js @@ -1,9 +1,10 @@ import lodashGet from 'lodash/get'; import React from 'react'; -import {View} from 'react-native'; +import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import _ from 'underscore'; +import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; @@ -27,12 +28,20 @@ import Section from '../../components/Section'; import CONST from '../../CONST'; import Button from '../../components/Button'; import MenuItem from '../../components/MenuItem'; +import Enable2FAPrompt from './Enable2FAPrompt'; const propTypes = { ...withLocalizePropTypes, /** Bank account currently in setup */ reimbursementAccount: reimbursementAccountPropTypes, + + /** User's account who is setting up bank account */ + account: PropTypes.shape({ + + /** If user has Two factor authentication enabled */ + requiresTwoFactorAuth: PropTypes.bool, + }), }; const defaultProps = { @@ -41,6 +50,9 @@ const defaultProps = { errors: {}, maxAttemptsReached: false, }, + account: { + requiresTwoFactorAuth: false, + }, }; class ValidationStep extends React.Component { @@ -117,6 +129,7 @@ class ValidationStep extends React.Component { const maxAttemptsReached = lodashGet(this.props, 'reimbursementAccount.maxAttemptsReached'); const isVerifying = !maxAttemptsReached && state === BankAccount.STATE.VERIFYING; + const requiresTwoFactorAuth = lodashGet(this.props, 'account.requiresTwoFactorAuth'); return ( @@ -160,7 +173,7 @@ class ValidationStep extends React.Component { {this.props.translate('validationStep.descriptionCTA')} - + + {!requiresTwoFactorAuth && ( + + + + )} )} {isVerifying && ( - +
-
+ {!requiresTwoFactorAuth && ( + + )} + )}
); @@ -229,5 +250,8 @@ export default compose( reimbursementAccount: { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, }, + account: { + key: ONYXKEYS.ACCOUNT, + }, }), )(ValidationStep); From 4443f977323b1f98e828ec24f2d6e2418a21f867 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 6 Jan 2023 00:19:56 +0530 Subject: [PATCH 2/6] Pass appropriate URL params --- src/pages/ReimbursementAccount/Enable2FAPrompt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/Enable2FAPrompt.js b/src/pages/ReimbursementAccount/Enable2FAPrompt.js index 6b89006bb43d6..d302f8305b724 100644 --- a/src/pages/ReimbursementAccount/Enable2FAPrompt.js +++ b/src/pages/ReimbursementAccount/Enable2FAPrompt.js @@ -22,7 +22,7 @@ const Enable2FAPrompt = props => ( { title: props.translate('validationStep.secureYourAccount'), onPress: () => { - Link.openOldDotLink(`settings?param={"section":"account","action":"enableTwoFactorAuth","exitTo":"${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.BANK_ACCOUNT}"}`); + Link.openOldDotLink(`settings?param={"section":"account","action":"enableTwoFactorAuth","exitTo":"${ROUTES.getBankAccountRoute()}","isFromNewDot":"true"}`); }, icon: Expensicons.Shield, shouldShowRightIcon: true, From 0e318a18e6ad6b9cf90f1fb75a71dfb397da4c5b Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 6 Jan 2023 00:20:11 +0530 Subject: [PATCH 3/6] Force login and refresh authToken --- src/pages/LogOutPreviousUserPage.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index d9d36602721dd..de2629d9d9b98 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -20,11 +20,24 @@ class LogOutPreviousUserPage extends Component { componentDidMount() { Linking.getInitialURL() .then((transitionURL) => { + const sessionEmail = lodashGet(this.props.session, 'email', ''); 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 OlDot + // 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(this.props, 'route.params.shouldForceLogin', '') === 'true'; + if (shouldForceLogin) { + const email = lodashGet(this.props, 'route.params.email', ''); + const shortLivedAuthToken = lodashGet(this.props, 'route.params.shortLivedAuthToken', ''); + Session.signInWithShortLivedAuthToken(email, shortLivedAuthToken); + } }); } From 445e8da0e028d67a141baa18d712d71918662126 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 6 Jan 2023 00:34:44 +0530 Subject: [PATCH 4/6] Lint fix --- src/pages/LogOutPreviousUserPage.js | 1 - src/pages/ReimbursementAccount/Enable2FAPrompt.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index de2629d9d9b98..419954a00e9b2 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -20,7 +20,6 @@ class LogOutPreviousUserPage extends Component { componentDidMount() { Linking.getInitialURL() .then((transitionURL) => { - const sessionEmail = lodashGet(this.props.session, 'email', ''); const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionURL, sessionEmail); diff --git a/src/pages/ReimbursementAccount/Enable2FAPrompt.js b/src/pages/ReimbursementAccount/Enable2FAPrompt.js index d302f8305b724..809e84ab349b0 100644 --- a/src/pages/ReimbursementAccount/Enable2FAPrompt.js +++ b/src/pages/ReimbursementAccount/Enable2FAPrompt.js @@ -7,7 +7,6 @@ import * as Expensicons from '../../components/Icon/Expensicons'; import * as Illustrations from '../../components/Icon/Illustrations'; import Section from '../../components/Section'; import * as Link from '../../libs/actions/Link'; -import CONFIG from '../../CONFIG'; import ROUTES from '../../ROUTES'; import themeColors from '../../styles/themes/default'; From e81c7eb1b6b3b9895c3b64cf3c915b1a0051a2d9 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 6 Jan 2023 19:24:37 +0530 Subject: [PATCH 5/6] Encode URL --- src/pages/ReimbursementAccount/Enable2FAPrompt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/Enable2FAPrompt.js b/src/pages/ReimbursementAccount/Enable2FAPrompt.js index 809e84ab349b0..51c62b8957c1b 100644 --- a/src/pages/ReimbursementAccount/Enable2FAPrompt.js +++ b/src/pages/ReimbursementAccount/Enable2FAPrompt.js @@ -21,7 +21,7 @@ const Enable2FAPrompt = props => ( { title: props.translate('validationStep.secureYourAccount'), onPress: () => { - Link.openOldDotLink(`settings?param={"section":"account","action":"enableTwoFactorAuth","exitTo":"${ROUTES.getBankAccountRoute()}","isFromNewDot":"true"}`); + Link.openOldDotLink(encodeURI(`settings?param={"section":"account","action":"enableTwoFactorAuth","exitTo":"${ROUTES.getBankAccountRoute()}","isFromNewDot":"true"}`)); }, icon: Expensicons.Shield, shouldShowRightIcon: true, From 18f932c468046cebbbc4f32b2c822fa946097ad5 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 20 Jan 2023 18:42:40 +0530 Subject: [PATCH 6/6] Cleanup Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/pages/LogOutPreviousUserPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 419954a00e9b2..843930b14e8a0 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -27,7 +27,7 @@ class LogOutPreviousUserPage extends Component { Session.signOutAndRedirectToSignIn(); } - // We need to signin and fetch a new authToken, if a user was already authenticated in NewDot, and was redirected to OlDot + // 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