Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
import { authenticator } from 'otplib';
|
|
|
|
import { INSTANCE_OWNER_CREDENTIALS } from '../../../../config/test-users';
|
|
import { test, expect } from '../../../../fixtures/base';
|
|
|
|
test.use({ capability: { env: { TEST_ISOLATION: 'two-factor-auth' } } });
|
|
|
|
const TEST_DATA = {
|
|
NEW_EMAIL: 'newemail@test.com',
|
|
NEW_FIRST_NAME: 'newFirstName',
|
|
NEW_LAST_NAME: 'newLastName',
|
|
};
|
|
|
|
const NOTIFICATIONS = {
|
|
PERSONAL_DETAILS_UPDATED: 'Personal details updated',
|
|
};
|
|
|
|
const { email, password, mfaSecret, mfaRecoveryCodes } = INSTANCE_OWNER_CREDENTIALS;
|
|
const RECOVERY_CODE = mfaRecoveryCodes![0];
|
|
|
|
test.describe(
|
|
'Two-factor authentication @auth:none @db:reset',
|
|
{
|
|
annotation: [{ type: 'owner', description: 'Identity & Access' }],
|
|
},
|
|
() => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
test('Should be able to login with MFA code', async ({ n8n }) => {
|
|
await n8n.mfaComposer.enableMfa(email, password, mfaSecret!);
|
|
await n8n.sideBar.signOutFromWorkflows();
|
|
|
|
await n8n.mfaComposer.loginWithMfaCode(email, password, mfaSecret!);
|
|
|
|
await expect(n8n.page).toHaveURL(/workflows/);
|
|
});
|
|
|
|
test('Should be able to login with MFA recovery code', async ({ n8n }) => {
|
|
await n8n.mfaComposer.enableMfa(email, password, mfaSecret!);
|
|
await n8n.sideBar.signOutFromWorkflows();
|
|
|
|
await n8n.mfaComposer.loginWithMfaRecoveryCode(email, password, RECOVERY_CODE);
|
|
|
|
await expect(n8n.page).toHaveURL(/workflows/);
|
|
});
|
|
|
|
test('Should be able to disable MFA in account with MFA code', async ({ n8n }) => {
|
|
await n8n.mfaComposer.enableMfa(email, password, mfaSecret!);
|
|
await n8n.sideBar.signOutFromWorkflows();
|
|
|
|
await n8n.mfaComposer.loginWithMfaCode(email, password, mfaSecret!);
|
|
|
|
const disableToken = authenticator.generate(mfaSecret!);
|
|
await n8n.settingsPersonal.triggerDisableMfa();
|
|
await n8n.settingsPersonal.fillMfaCodeAndSave(disableToken);
|
|
|
|
await expect(n8n.settingsPersonal.getEnableMfaButton()).toBeVisible();
|
|
});
|
|
|
|
test('Should prompt for MFA code when email changes', async ({ n8n }) => {
|
|
await n8n.mfaComposer.enableMfa(email, password, mfaSecret!);
|
|
|
|
await n8n.settingsPersonal.goto();
|
|
await n8n.settingsPersonal.fillEmail(TEST_DATA.NEW_EMAIL);
|
|
await n8n.settingsPersonal.pressEnterOnEmail();
|
|
|
|
const mfaCode = authenticator.generate(mfaSecret!);
|
|
await n8n.settingsPersonal.fillMfaCodeAndSave(mfaCode);
|
|
|
|
await expect(
|
|
n8n.notifications.getNotificationByTitleOrContent(NOTIFICATIONS.PERSONAL_DETAILS_UPDATED),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('Should prompt for MFA recovery code when email changes', async ({ n8n }) => {
|
|
await n8n.mfaComposer.enableMfa(email, password, mfaSecret!);
|
|
|
|
await n8n.settingsPersonal.goto();
|
|
await n8n.settingsPersonal.fillEmail(TEST_DATA.NEW_EMAIL);
|
|
await n8n.settingsPersonal.pressEnterOnEmail();
|
|
|
|
await expect(n8n.settingsPersonal.getMfaCodeOrRecoveryCodeInput()).toBeVisible();
|
|
});
|
|
|
|
test('Should not prompt for MFA code or recovery code when first name or last name changes', async ({
|
|
n8n,
|
|
}) => {
|
|
await n8n.mfaComposer.enableMfa(email, password, mfaSecret!);
|
|
|
|
await n8n.settingsPersonal.updateFirstAndLastName(
|
|
TEST_DATA.NEW_FIRST_NAME,
|
|
TEST_DATA.NEW_LAST_NAME,
|
|
);
|
|
|
|
await expect(
|
|
n8n.notifications.getNotificationByTitleOrContent(NOTIFICATIONS.PERSONAL_DETAILS_UPDATED),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('Should be able to disable MFA in account with recovery code', async ({ n8n }) => {
|
|
await n8n.mfaComposer.enableMfa(email, password, mfaSecret!);
|
|
await n8n.sideBar.signOutFromWorkflows();
|
|
|
|
await n8n.mfaComposer.loginWithMfaCode(email, password, mfaSecret!);
|
|
|
|
await n8n.settingsPersonal.triggerDisableMfa();
|
|
await n8n.settingsPersonal.fillMfaCodeAndSave(RECOVERY_CODE);
|
|
|
|
await expect(n8n.settingsPersonal.getEnableMfaButton()).toBeVisible();
|
|
});
|
|
},
|
|
);
|