diff --git a/src/languages/de.ts b/src/languages/de.ts index cf1a0c44ce00..5dbc0c6ac462 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2552,7 +2552,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: 'Scannen Sie den QR-Code mit Ihrem neuen Gerät und geben Sie dann den Code ein, um die Einrichtung abzuschließen.', downloadCodes: 'Codes herunterladen', copyCodes: 'Codes kopieren', - twoFactorAuthIsRequiredNetSuiteDescription: 'Aus Sicherheitsgründen erfordert NetSuite zur Verbindung der Integration eine Zwei-Faktor-Authentifizierung.', }, recoveryCodeForm: { error: { diff --git a/src/languages/el.ts b/src/languages/el.ts index d9677ed178a0..744a1e5585bc 100644 --- a/src/languages/el.ts +++ b/src/languages/el.ts @@ -2608,7 +2608,6 @@ const translations: TranslationDeepObject = { verifyOldDeviceDescriptionWithRecovery: 'Εισαγάγετε έναν έγκυρο κωδικό ανάκτησης για να επιβεβαιώσετε ότι έχετε πρόσβαση στον λογαριασμό σας.', verifyNewDeviceTitle: 'Ρύθμιση νέας συσκευής', verifyNewDeviceDescription: 'Σαρώστε τον κωδικό QR με τη νέα σας συσκευή και κατόπιν εισαγάγετε τον κωδικό για να ολοκληρώσετε τη ρύθμιση.', - twoFactorAuthIsRequiredNetSuiteDescription: 'Για λόγους ασφαλείας, το NetSuite απαιτεί έλεγχο ταυτότητας δύο παραγόντων για τη σύνδεση της ενοποίησης.', }, recoveryCodeForm: { error: { diff --git a/src/languages/en.ts b/src/languages/en.ts index ae765caad789..f28ffa5cd396 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2646,7 +2646,6 @@ const translations = { enableTwoFactorAuth: 'Enable two-factor authentication', pleaseEnableTwoFactorAuth: 'Please enable two-factor authentication.', twoFactorAuthIsRequiredDescription: 'For security purposes, Xero requires two-factor authentication to connect the integration.', - twoFactorAuthIsRequiredNetSuiteDescription: 'For security purposes, NetSuite requires two-factor authentication to connect the integration.', twoFactorAuthIsRequiredForAdminsHeader: 'Two-factor authentication required', twoFactorAuthIsRequiredForAdminsTitle: 'Please enable two-factor authentication', twoFactorAuthIsRequiredXero: 'Your Xero accounting connection requires two-factor authentication.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 8f2ae5efdf8e..2335a97d235c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2472,7 +2472,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: 'Escanea el código QR con tu nuevo dispositivo y luego introduce el código para completar la configuración.', downloadCodes: 'Descargar códigos', copyCodes: 'Copiar códigos', - twoFactorAuthIsRequiredNetSuiteDescription: 'Por motivos de seguridad, NetSuite requiere autenticación de dos factores para conectar la integración.', }, recoveryCodeForm: { error: { diff --git a/src/languages/fr.ts b/src/languages/fr.ts index f409f8ba20d7..376a5ce61b12 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2556,7 +2556,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: 'Scannez le code QR avec votre nouvel appareil, puis saisissez le code pour terminer la configuration.', downloadCodes: 'Télécharger les codes', copyCodes: 'Copier les codes', - twoFactorAuthIsRequiredNetSuiteDescription: "Pour des raisons de sécurité, NetSuite exige une authentification à deux facteurs pour connecter l'intégration.", }, recoveryCodeForm: { error: { diff --git a/src/languages/it.ts b/src/languages/it.ts index 0a58b343588d..d8894d9af458 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2543,7 +2543,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: 'Scansiona il codice QR con il tuo nuovo dispositivo, poi inserisci il codice per completare la configurazione.', downloadCodes: 'Scarica codici', copyCodes: 'Copia codici', - twoFactorAuthIsRequiredNetSuiteDescription: 'Per motivi di sicurezza, NetSuite richiede l’autenticazione a due fattori per collegare l’integrazione.', }, recoveryCodeForm: { error: { diff --git a/src/languages/ja.ts b/src/languages/ja.ts index a1ddac462ae5..4d1a36ca6ce1 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2525,7 +2525,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: '新しいデバイスでQRコードをスキャンし、表示されたコードを入力して設定を完了してください。', downloadCodes: 'コードをダウンロード', copyCodes: 'コードをコピー', - twoFactorAuthIsRequiredNetSuiteDescription: 'セキュリティ上の理由により、連携を接続するには NetSuite で二要素認証が必要です。', }, recoveryCodeForm: { error: { diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 0e3c9c7f419f..e87903b03f55 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2544,7 +2544,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: 'Scan de QR-code met je nieuwe apparaat en voer daarna de code in om de installatie te voltooien.', downloadCodes: 'Codes downloaden', copyCodes: 'Codes kopiëren', - twoFactorAuthIsRequiredNetSuiteDescription: 'Om veiligheidsredenen vereist NetSuite tweeledige verificatie om de koppeling te maken.', }, recoveryCodeForm: { error: { diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 8033f8c99463..55f579a0ac94 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2581,7 +2581,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: 'Zeskanuj kod QR nowym urządzeniem, a następnie wpisz ten kod, aby zakończyć konfigurację.', downloadCodes: 'Pobierz kody', copyCodes: 'Skopiuj kody', - twoFactorAuthIsRequiredNetSuiteDescription: 'Ze względów bezpieczeństwa NetSuite wymaga uwierzytelniania dwuskładnikowego, aby połączyć integrację.', }, recoveryCodeForm: { error: { diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 5ef0551bd839..a6937f82a051 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2537,7 +2537,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: 'Escaneie o código QR com seu novo dispositivo e depois insira o código para concluir a configuração.', downloadCodes: 'Baixar códigos', copyCodes: 'Copiar códigos', - twoFactorAuthIsRequiredNetSuiteDescription: 'Por motivos de segurança, o NetSuite exige autenticação em duas etapas para conectar a integração.', }, recoveryCodeForm: { error: { diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 5ba1a8ca7b10..6060f55e1771 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2460,7 +2460,6 @@ const translations: TranslationDeepObject = { verifyNewDeviceDescription: '使用新设备扫描二维码,然后输入代码完成设置。', downloadCodes: '下载代码', copyCodes: '复制代码', - twoFactorAuthIsRequiredNetSuiteDescription: '出于安全考虑,NetSuite 要求使用双重身份验证来连接此集成。', }, recoveryCodeForm: { error: { diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx index 64afd3d0069c..1fc7e2b9c065 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx @@ -2,7 +2,6 @@ import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import RenderHTML from '@components/RenderHTML'; -import RequireTwoFactorAuthenticationModal from '@components/RequireTwoFactorAuthenticationModal'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; @@ -11,13 +10,11 @@ import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import useTwoFactorAuthRoute from '@hooks/useTwoFactorAuthRoute'; import {shouldUseUpdateNetSuiteTokens} from '@libs/actions/connections'; import {connectPolicyToNetSuite, updateNetSuiteTokens} from '@libs/actions/connections/NetSuiteCommands'; import {isMobileSafari} from '@libs/Browser'; import {addErrorMessage} from '@libs/ErrorUtils'; -import Navigation from '@libs/Navigation/Navigation'; import Parser from '@libs/Parser'; import NetSuiteTokenAuthenticationLink from '@pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteTokenAuthenticationLink'; @@ -27,7 +24,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/NetSuiteTokenInputForm'; -import React, {useCallback, useState} from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import connectToNetSuiteOAuthSetup from './connectToNetSuiteOAuthSetup'; @@ -38,9 +35,6 @@ function NetSuiteTokenInputForm({onNext, policyID, isOAuthFlow, shouldShowTokenA const policy = usePolicy(policyID); const {inputCallbackRef} = useAutoFocusInput(); const {environmentURL} = useEnvironment(); - const {is2FAEnabled, getTwoFactorAuthRoute} = useTwoFactorAuthRoute(); - - const [isRequire2FAModalOpen, setIsRequire2FAModalOpen] = useState(false); const formInputs = isOAuthFlow ? [INPUT_IDS.NETSUITE_ACCOUNT_ID] : Object.values(INPUT_IDS); @@ -66,10 +60,6 @@ function NetSuiteTokenInputForm({onNext, policyID, isOAuthFlow, shouldShowTokenA } if (isOAuthFlow) { - if (!is2FAEnabled) { - setIsRequire2FAModalOpen(true); - return; - } connectToNetSuiteOAuthSetup(policyID, formValues[INPUT_IDS.NETSUITE_ACCOUNT_ID], environmentURL); return; } @@ -81,7 +71,7 @@ function NetSuiteTokenInputForm({onNext, policyID, isOAuthFlow, shouldShowTokenA } onNext(); }, - [onNext, policyID, policy, isOAuthFlow, is2FAEnabled, environmentURL], + [onNext, policyID, policy, isOAuthFlow, environmentURL], ); return ( @@ -126,19 +116,6 @@ function NetSuiteTokenInputForm({onNext, policyID, isOAuthFlow, shouldShowTokenA ))} - {isOAuthFlow && !is2FAEnabled && ( - { - setIsRequire2FAModalOpen(false); - Navigation.navigate(getTwoFactorAuthRoute()); - }} - onCancel={() => { - setIsRequire2FAModalOpen(false); - }} - isVisible={isRequire2FAModalOpen} - description={translate('twoFactorAuth.twoFactorAuthIsRequiredNetSuiteDescription')} - /> - )} ); } diff --git a/tests/unit/pages/workspace/accounting/netsuite/NetSuiteTokenInputFormTest.tsx b/tests/unit/pages/workspace/accounting/netsuite/NetSuiteTokenInputFormTest.tsx index 39315d3c31fd..89ecc1c8ea2b 100644 --- a/tests/unit/pages/workspace/accounting/netsuite/NetSuiteTokenInputFormTest.tsx +++ b/tests/unit/pages/workspace/accounting/netsuite/NetSuiteTokenInputFormTest.tsx @@ -1,27 +1,21 @@ -import {act, render, screen} from '@testing-library/react-native'; - -import useTwoFactorAuthRoute from '@hooks/useTwoFactorAuthRoute'; +import {act, render} from '@testing-library/react-native'; import {shouldUseUpdateNetSuiteTokens} from '@libs/actions/connections'; import {connectPolicyToNetSuite, updateNetSuiteTokens} from '@libs/actions/connections/NetSuiteCommands'; -import Navigation from '@libs/Navigation/Navigation'; import connectToNetSuiteOAuthSetup from '@pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/connectToNetSuiteOAuthSetup'; import NetSuiteTokenInputForm from '@pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; import type {NetSuiteTokenInputForm as NetSuiteTokenInputFormType} from '@src/types/form/NetSuiteTokenInputForm'; import React from 'react'; -import {View} from 'react-native'; const POLICY_ID = '123'; const ACCOUNT_ID = 'TSTDRV1234567'; const TOKEN_ID = 'token-123'; const TOKEN_SECRET = 'secret-123'; const ENVIRONMENT_URL = 'https://new.expensify.com'; -const TWO_FACTOR_AUTH_ROUTE = ROUTES.SETTINGS_2FA_ENABLED; const FORM_VALUES = { netSuiteAccountID: ACCOUNT_ID, @@ -29,16 +23,7 @@ const FORM_VALUES = { netSuiteTokenSecret: TOKEN_SECRET, } as NetSuiteTokenInputFormType; -type Require2FAProps = { - isVisible: boolean; - onSubmit: () => void; - onCancel: () => void; -}; - -// `Mock`-prefixed bindings are allowed inside jest.mock factories, unlike regular imports. -const MockView = View; - -// Capture the props of the mocked children so the tests can drive submission and the modal. +// Capture the form props to submit credentials in tests. type MockFormProviderProps = { children: React.ReactNode; onSubmit: (values: NetSuiteTokenInputFormType) => void; @@ -50,9 +35,6 @@ type MockFormProviderProps = { const mockFormProps: {current: Omit | undefined} = { current: undefined, }; -const mockRequire2FAProps: {current: Require2FAProps | undefined} = { - current: undefined, -}; jest.mock('@hooks/useLocalize', () => () => ({ translate: (key: string) => key, @@ -75,7 +57,6 @@ jest.mock('@hooks/useAutoFocusInput', () => () => ({ inputCallbackRef: jest.fn(), })); jest.mock('@hooks/usePolicy', () => () => undefined); -jest.mock('@hooks/useTwoFactorAuthRoute'); jest.mock('@libs/actions/connections', () => ({ shouldUseUpdateNetSuiteTokens: jest.fn(() => false), })); @@ -84,9 +65,6 @@ jest.mock('@libs/actions/connections/NetSuiteCommands', () => ({ updateNetSuiteTokens: jest.fn(), })); jest.mock('@pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/connectToNetSuiteOAuthSetup', () => jest.fn()); -jest.mock('@libs/Navigation/Navigation', () => ({ - navigate: jest.fn(), -})); jest.mock('@components/RenderHTML', () => () => null); jest.mock('@components/Form/FormProvider', () => { function MockFormProvider({children, onSubmit, keyboardSubmitBehavior, shouldShowLoadingImmediatelyOnPress, submitButtonText}: MockFormProviderProps) { @@ -96,27 +74,13 @@ jest.mock('@components/Form/FormProvider', () => { return MockFormProvider; }); jest.mock('@components/Form/InputWrapper', () => () => null); -jest.mock('@components/RequireTwoFactorAuthenticationModal', () => ({isVisible, onSubmit, onCancel}: Require2FAProps) => { - mockRequire2FAProps.current = {isVisible, onSubmit, onCancel}; - return isVisible ? : null; -}); -const mockedUseTwoFactorAuthRoute = jest.mocked(useTwoFactorAuthRoute); const mockedShouldUseUpdateNetSuiteTokens = jest.mocked(shouldUseUpdateNetSuiteTokens); const mockedConnectPolicyToNetSuite = jest.mocked(connectPolicyToNetSuite); const mockedUpdateNetSuiteTokens = jest.mocked(updateNetSuiteTokens); const mockedConnectToNetSuiteOAuthSetup = jest.mocked(connectToNetSuiteOAuthSetup); -const mockedNavigate = jest.mocked(Navigation.navigate); -const mockedGetTwoFactorAuthRoute = jest.fn(() => TWO_FACTOR_AUTH_ROUTE); const mockedOnNext = jest.fn(); -function set2FAEnabled(is2FAEnabled: boolean) { - mockedUseTwoFactorAuthRoute.mockReturnValue({ - is2FAEnabled, - getTwoFactorAuthRoute: mockedGetTwoFactorAuthRoute, - }); -} - function renderForm(isOAuthFlow: boolean) { render( { beforeEach(() => { jest.clearAllMocks(); mockFormProps.current = undefined; - mockRequire2FAProps.current = undefined; - mockedGetTwoFactorAuthRoute.mockReturnValue(TWO_FACTOR_AUTH_ROUTE); mockedShouldUseUpdateNetSuiteTokens.mockReturnValue(false); }); - describe('in the OAuth flow with 2FA enabled', () => { - beforeEach(() => { - set2FAEnabled(true); - }); - + describe('in the OAuth flow', () => { it('hands off to the OAuth setup with the policy, account ID and environment URL', () => { renderForm(true); submitForm(); @@ -171,13 +129,6 @@ describe('NetSuiteTokenInputForm', () => { expect(mockedOnNext).not.toHaveBeenCalled(); }); - it('does not show the 2FA requirement modal', () => { - renderForm(true); - submitForm(); - - expect(screen.queryByTestId('require-2fa-modal')).toBeNull(); - }); - it('submits synchronously so the setup link opens inside the tap gesture and is not popup-blocked', () => { renderForm(true); @@ -194,54 +145,13 @@ describe('NetSuiteTokenInputForm', () => { }); }); - describe('in the OAuth flow with 2FA not enabled', () => { - beforeEach(() => { - set2FAEnabled(false); - }); - - it('blocks the OAuth handoff and shows the 2FA requirement modal', () => { - renderForm(true); - submitForm(); - - expect(screen.getByTestId('require-2fa-modal')).toBeOnTheScreen(); - expect(mockedConnectToNetSuiteOAuthSetup).not.toHaveBeenCalled(); - }); - - it('navigates to the 2FA route when the modal is submitted', () => { - renderForm(true); - submitForm(); - expect(screen.getByTestId('require-2fa-modal')).toBeOnTheScreen(); - - act(() => mockRequire2FAProps.current?.onSubmit()); - - expect(mockedGetTwoFactorAuthRoute).toHaveBeenCalled(); - expect(mockedNavigate).toHaveBeenCalledWith(TWO_FACTOR_AUTH_ROUTE); - }); - - it('closes the modal without navigating when cancelled', () => { - renderForm(true); - submitForm(); - expect(screen.getByTestId('require-2fa-modal')).toBeOnTheScreen(); - - act(() => mockRequire2FAProps.current?.onCancel()); - - expect(mockedNavigate).not.toHaveBeenCalled(); - expect(screen.queryByTestId('require-2fa-modal')).toBeNull(); - }); - }); - describe('in the token-based authentication flow', () => { - beforeEach(() => { - set2FAEnabled(false); - }); - - it('writes the token-based credentials without requiring 2FA', () => { + it('writes the token-based credentials', () => { renderForm(false); submitForm(); expect(mockedConnectPolicyToNetSuite).toHaveBeenCalledWith(POLICY_ID, FORM_VALUES); expect(mockedConnectToNetSuiteOAuthSetup).not.toHaveBeenCalled(); - expect(screen.queryByTestId('require-2fa-modal')).toBeNull(); }); it('advances the wizard so the RHP is dismissed', () => {