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')} + + + + + + +