diff --git a/src/CONST.ts b/src/CONST.ts
index 603c024ba33e..a494f01073c2 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -3881,6 +3881,7 @@ const CONST = {
SUCCESS: 'SUCCESS',
ENABLED: 'ENABLED',
DISABLED: 'DISABLED',
+ GETCODE: 'GETCODE',
},
DELEGATE_ROLE: {
SUBMITTER: 'submitter',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 48953757deca..448d733f5214 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -1115,7 +1115,7 @@ export default {
twoFactorAuthEnabled: 'Two-factor authentication enabled',
whatIsTwoFactorAuth: 'Two-factor authentication (2FA) helps keep your account safe. When logging in, you’ll need to enter a code generated by your preferred authenticator app.',
disableTwoFactorAuth: 'Disable two-factor authentication',
- disableTwoFactorAuthConfirmation: 'Two-factor authentication keeps your account more secure. Are you sure you want to disable it?',
+ explainProcessToRemove: 'In order to disable two-factor authentication (2FA), please enter a valid code from your authentication app.',
disabled: 'Two-factor authentication is now disabled',
noAuthenticatorApp: 'You’ll no longer require an authenticator app to log into Expensify.',
stepCodes: 'Recovery codes',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 2ab6deacda47..46c6dc720077 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1120,7 +1120,7 @@ export default {
whatIsTwoFactorAuth:
'La autenticación de dos factores (2FA) ayuda a mantener tu cuenta segura. Al iniciar sesión, deberás ingresar un código generado por tu aplicación de autenticación preferida.',
disableTwoFactorAuth: 'Deshabilitar la autenticación de dos factores',
- disableTwoFactorAuthConfirmation: 'La autenticación de dos factores mantiene tu cuenta más segura. ¿Estás seguro de que quieres desactivarla?',
+ explainProcessToRemove: 'Para deshabilitar la autenticación de dos factores (2FA), por favor introduce un código válido de tu aplicación de autenticación.',
disabled: 'La autenticación de dos factores está ahora deshabilitada',
noAuthenticatorApp: 'Ya no necesitarás una aplicación de autenticación para iniciar sesión en Expensify.',
stepCodes: 'Códigos de recuperación',
diff --git a/src/libs/API/parameters/DisableTwoFactorAuthParams.ts b/src/libs/API/parameters/DisableTwoFactorAuthParams.ts
new file mode 100644
index 000000000000..04fd378272fb
--- /dev/null
+++ b/src/libs/API/parameters/DisableTwoFactorAuthParams.ts
@@ -0,0 +1,5 @@
+type DisableTwoFactorAuthParams = {
+ twoFactorAuthCode: string;
+};
+
+export default DisableTwoFactorAuthParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 9294ab0c98a1..24b708d6b81b 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -84,6 +84,7 @@ export type {default as ValidateBankAccountWithTransactionsParams} from './Valid
export type {default as ValidateLoginParams} from './ValidateLoginParams';
export type {default as ValidateSecondaryLoginParams} from './ValidateSecondaryLoginParams';
export type {default as ValidateTwoFactorAuthParams} from './ValidateTwoFactorAuthParams';
+export type {default as DisableTwoFactorAuthParams} from './DisableTwoFactorAuthParams';
export type {default as VerifyIdentityForBankAccountParams} from './VerifyIdentityForBankAccountParams';
export type {default as AnswerQuestionsForWalletParams} from './AnswerQuestionsForWalletParams';
export type {default as AddCommentOrAttachementParams} from './AddCommentOrAttachementParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index edfb4ebebacb..37e23dbcffa5 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -419,7 +419,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.REQUEST_UNLINK_VALIDATION_LINK]: Parameters.RequestUnlinkValidationLinkParams;
[WRITE_COMMANDS.UNLINK_LOGIN]: Parameters.UnlinkLoginParams;
[WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH]: null;
- [WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH]: null;
+ [WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH]: Parameters.DisableTwoFactorAuthParams;
[WRITE_COMMANDS.ADD_COMMENT]: Parameters.AddCommentOrAttachementParams;
[WRITE_COMMANDS.ADD_ATTACHMENT]: Parameters.AddCommentOrAttachementParams;
[WRITE_COMMANDS.ADD_TEXT_AND_ATTACHMENT]: Parameters.AddCommentOrAttachementParams;
diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts
index 32809b2f7500..e905464e551f 100644
--- a/src/libs/actions/Session/index.ts
+++ b/src/libs/actions/Session/index.ts
@@ -12,6 +12,7 @@ import type {
BeginAppleSignInParams,
BeginGoogleSignInParams,
BeginSignInParams,
+ DisableTwoFactorAuthParams,
RequestAccountValidationLinkParams,
RequestNewValidateCodeParams,
RequestUnlinkValidationLinkParams,
@@ -877,7 +878,7 @@ function unlinkLogin(accountID: number, validateCode: string) {
/**
* Toggles two-factor authentication based on the `enable` parameter
*/
-function toggleTwoFactorAuth(enable: boolean) {
+function toggleTwoFactorAuth(enable: boolean, twoFactorAuthCode = '') {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -894,6 +895,9 @@ function toggleTwoFactorAuth(enable: boolean) {
key: ONYXKEYS.ACCOUNT,
value: {
isLoading: false,
+
+ // When disabling 2FA, the user needs to end up on the step that confirms the setting was disabled
+ twoFactorAuthStep: enable ? undefined : CONST.TWO_FACTOR_AUTH_STEPS.DISABLED,
},
},
];
@@ -908,7 +912,16 @@ function toggleTwoFactorAuth(enable: boolean) {
},
];
- API.write(enable ? WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH : WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH, null, {optimisticData, successData, failureData});
+ if (enable) {
+ API.write(WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH, null, {optimisticData, successData, failureData});
+ return;
+ }
+
+ // A 2FA code is required to disable 2FA
+ const params: DisableTwoFactorAuthParams = {twoFactorAuthCode};
+
+ // eslint-disable-next-line rulesdir/no-multiple-api-calls
+ API.write(WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH, params, {optimisticData, successData, failureData});
}
function updateAuthTokenAndOpenApp(authToken?: string, encryptedAuthToken?: string) {
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx b/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx
index ddd99f8fb54e..f887e700adb0 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.tsx
@@ -1,6 +1,5 @@
-import React, {useState} from 'react';
+import React from 'react';
import {View} from 'react-native';
-import ConfirmModal from '@components/ConfirmModal';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import ScrollView from '@components/ScrollView';
@@ -11,13 +10,11 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper';
import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth';
-import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
function EnabledStep() {
const theme = useTheme();
const styles = useThemeStyles();
- const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false);
const {setStep} = useTwoFactorAuthContext();
@@ -33,7 +30,7 @@ function EnabledStep() {
{
title: translate('twoFactorAuth.disableTwoFactorAuth'),
onPress: () => {
- setIsConfirmModalVisible(true);
+ setStep(CONST.TWO_FACTOR_AUTH_STEPS.GETCODE);
},
icon: Expensicons.Close,
iconFill: theme.danger,
@@ -46,22 +43,6 @@ function EnabledStep() {
{translate('twoFactorAuth.whatIsTwoFactorAuth')}
- {
- setIsConfirmModalVisible(false);
- setStep(CONST.TWO_FACTOR_AUTH_STEPS.DISABLED);
- Session.toggleTwoFactorAuth(false);
- }}
- onCancel={() => setIsConfirmModalVisible(false)}
- onModalHide={() => setIsConfirmModalVisible(false)}
- isVisible={isConfirmModalVisible}
- prompt={translate('twoFactorAuth.disableTwoFactorAuthConfirmation')}
- confirmText={translate('common.disable')}
- cancelText={translate('common.cancel')}
- shouldShowCancelButton
- danger
- />
);
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/GetCode.tsx b/src/pages/settings/Security/TwoFactorAuth/Steps/GetCode.tsx
new file mode 100644
index 000000000000..372df5d17f6d
--- /dev/null
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/GetCode.tsx
@@ -0,0 +1,72 @@
+import React, {useRef} from 'react';
+import {View} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import Button from '@components/Button';
+import FixedFooter from '@components/FixedFooter';
+import ScrollView from '@components/ScrollView';
+import Text from '@components/Text';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import type {BackToParams} from '@libs/Navigation/types';
+import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper';
+import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth';
+import TwoFactorAuthForm from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm';
+import type {BaseTwoFactorAuthFormOnyxProps, BaseTwoFactorAuthFormRef} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+
+type GetCodeProps = BaseTwoFactorAuthFormOnyxProps & BackToParams;
+
+function GetCode({account}: GetCodeProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+
+ const formRef = useRef(null);
+
+ const {setStep} = useTwoFactorAuthContext();
+
+ return (
+ setStep(CONST.TWO_FACTOR_AUTH_STEPS.ENABLED, CONST.ANIMATION_DIRECTION.OUT)}
+ onEntryTransitionEnd={() => formRef.current && formRef.current.focus()}
+ >
+
+
+ {translate('twoFactorAuth.explainProcessToRemove')}
+
+
+
+
+
+
+
+
+ );
+}
+
+GetCode.displayName = 'GetCode';
+
+export default withOnyx({
+ account: {key: ONYXKEYS.ACCOUNT},
+ user: {
+ key: ONYXKEYS.USER,
+ },
+})(GetCode);
diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx
index ece2e8fdf2f2..fe37aa9b228a 100644
--- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx
+++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx
@@ -12,9 +12,13 @@ import type {BaseTwoFactorAuthFormOnyxProps, BaseTwoFactorAuthFormRef} from './t
type BaseTwoFactorAuthFormProps = BaseTwoFactorAuthFormOnyxProps & {
autoComplete: AutoCompleteVariant;
+
+ // Set this to true in order to call the validateTwoFactorAuth action which is used when setting up 2FA for the first time.
+ // Set this to false in order to disable 2FA when a valid code is entered.
+ validateInsteadOfDisable?: boolean;
};
-function BaseTwoFactorAuthForm({account, autoComplete}: BaseTwoFactorAuthFormProps, ref: ForwardedRef) {
+function BaseTwoFactorAuthForm({account, autoComplete, validateInsteadOfDisable}: BaseTwoFactorAuthFormProps, ref: ForwardedRef) {
const {translate} = useLocalize();
const [formError, setFormError] = useState<{twoFactorAuthCode?: string}>({});
const [twoFactorAuthCode, setTwoFactorAuthCode] = useState('');
@@ -54,8 +58,13 @@ function BaseTwoFactorAuthForm({account, autoComplete}: BaseTwoFactorAuthFormPro
}
setFormError({});
- Session.validateTwoFactorAuth(twoFactorAuthCode, shouldClearData);
- }, [twoFactorAuthCode, shouldClearData, translate]);
+
+ if (validateInsteadOfDisable !== false) {
+ Session.validateTwoFactorAuth(twoFactorAuthCode, shouldClearData);
+ return;
+ }
+ Session.toggleTwoFactorAuth(false, twoFactorAuthCode);
+ }, [twoFactorAuthCode, validateInsteadOfDisable, translate, shouldClearData]);
useImperativeHandle(ref, () => ({
validateAndSubmitForm() {
diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.android.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.android.tsx
index 55d8850d3f1d..f25f8756aede 100644
--- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.android.tsx
+++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.android.tsx
@@ -2,11 +2,12 @@ import React from 'react';
import BaseTwoFactorAuthForm from './BaseTwoFactorAuthForm';
import type {TwoFactorAuthFormProps} from './types';
-function TwoFactorAuthForm({innerRef}: TwoFactorAuthFormProps) {
+function TwoFactorAuthForm({innerRef, validateInsteadOfDisable}: TwoFactorAuthFormProps) {
return (
);
}
diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.tsx
index e12288b900b0..f72da5d873a0 100644
--- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.tsx
+++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/index.tsx
@@ -2,11 +2,12 @@ import React from 'react';
import BaseTwoFactorAuthForm from './BaseTwoFactorAuthForm';
import type {TwoFactorAuthFormProps} from './types';
-function TwoFactorAuthForm({innerRef}: TwoFactorAuthFormProps) {
+function TwoFactorAuthForm({innerRef, validateInsteadOfDisable}: TwoFactorAuthFormProps) {
return (
);
}
diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts
index 97510a2a3a29..14b5efa1feeb 100644
--- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts
+++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/types.ts
@@ -14,6 +14,10 @@ type BaseTwoFactorAuthFormRef = {
type TwoFactorAuthFormProps = {
innerRef: ForwardedRef;
+
+ // Set this to true in order to call the validateTwoFactorAuth action which is used when setting up 2FA for the first time.
+ // Set this to false in order to disable 2FA when a valid code is entered.
+ validateInsteadOfDisable?: boolean;
};
export type {BaseTwoFactorAuthFormOnyxProps, TwoFactorAuthFormProps, BaseTwoFactorAuthFormRef};
diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthSteps.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthSteps.tsx
index 76b1e7cfb0f7..c49b88c2ac0e 100644
--- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthSteps.tsx
+++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthSteps.tsx
@@ -13,6 +13,7 @@ import type {TwoFactorAuthStep} from '@src/types/onyx/Account';
import CodesStep from './Steps/CodesStep';
import DisabledStep from './Steps/DisabledStep';
import EnabledStep from './Steps/EnabledStep';
+import GetCodeStep from './Steps/GetCode';
import SuccessStep from './Steps/SuccessStep';
import VerifyStep from './Steps/VerifyStep';
import TwoFactorAuthContext from './TwoFactorAuthContext';
@@ -62,6 +63,8 @@ function TwoFactorAuthSteps({account}: TwoFactorAuthStepProps) {
return ;
case CONST.TWO_FACTOR_AUTH_STEPS.DISABLED:
return ;
+ case CONST.TWO_FACTOR_AUTH_STEPS.GETCODE:
+ return ;
default:
return ;
}