mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-10 02:47:49 +00:00
Add playwright test for MFA login flow (#13146)
This commit is contained in:
1 parent
f36321a918
commit
581fef33bc
3 files changed
+73
-1
No files matched your search
@@ -163,6 +163,7 @@ export const test = baseTest.extend<TestFixtures, {}>({
|
||||
!url.includes('/api/user/me/token/') &&
|
||||
!url.includes('/api/auth/v1/auth/login') &&
|
||||
!url.includes('/api/auth/v1/auth/session') &&
|
||||
!url.includes('/api/auth/v1/auth/2fa/authenticate') &&
|
||||
!url.includes('/api/auth/v1/auth/provider/signup') &&
|
||||
!url.includes('/api/auth/v1/account/authenticators/totp') &&
|
||||
!url.includes('/api/auth/v1/account/password/change') &&
|
||||
|
||||
@@ -44,6 +44,15 @@ export const engineeruser: UserType = {
|
||||
testcred: 'partsonly'
|
||||
};
|
||||
|
||||
/*
|
||||
* Demo dataset user with a pre-configured TOTP authenticator
|
||||
*/
|
||||
export const mfauser: UserType & { totpSecret: string } = {
|
||||
username: 'mfauser',
|
||||
testcred: 'mfauserpassword',
|
||||
totpSecret: 'TFFPZIB4VAOKMNMYWIYVMPGBE24RZIHF'
|
||||
};
|
||||
|
||||
export const mockOidcPort = 9950;
|
||||
export const mockOidcUrl = `http://localhost:${mockOidcPort}`;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
import { TOTP } from 'otpauth';
|
||||
import { expect, test } from './baseFixtures.js';
|
||||
import { engineeruser, logoutUrl, noaccessuser } from './defaults.js';
|
||||
import { engineeruser, logoutUrl, mfauser, noaccessuser } from './defaults.js';
|
||||
import { navigate, openDetailAction } from './helpers.js';
|
||||
import { doLogin } from './login.js';
|
||||
|
||||
@@ -502,3 +502,65 @@ test('Login - MFA - TOTP', async ({ page }) => {
|
||||
.getByText('No multi-factor tokens configured for this account')
|
||||
.waitFor();
|
||||
});
|
||||
|
||||
// Test login for a user with a pre-configured TOTP authenticator
|
||||
test('Login - MFA - TOTP Login', async ({ page }) => {
|
||||
await page.context().clearCookies();
|
||||
await navigate(page, logoutUrl, { waitUntil: 'load' });
|
||||
await page.waitForURL('**/web/login');
|
||||
|
||||
await page.getByLabel('login-username').fill(mfauser.username);
|
||||
await page.getByLabel('login-password').fill(mfauser.testcred);
|
||||
await page.getByRole('button', { name: 'Log In' }).click();
|
||||
|
||||
// User should be redirected to the MFA page
|
||||
await page.waitForURL('**/web/mfa');
|
||||
await page.getByText('Multi-Factor Authentication').waitFor();
|
||||
|
||||
const codeInput = page.getByRole('textbox', { name: 'TOTP Code' });
|
||||
await codeInput.waitFor();
|
||||
|
||||
// Enter an incorrect code
|
||||
await codeInput.fill('ABCDEF');
|
||||
await page.getByRole('button', { name: 'Log in' }).click();
|
||||
await page.getByText('Incorrect code.').waitFor();
|
||||
|
||||
// We should still be on the MFA page
|
||||
await expect(page).toHaveURL(/\/web\/mfa/);
|
||||
|
||||
const totp = new TOTP({
|
||||
secret: mfauser.totpSecret,
|
||||
digits: 6,
|
||||
period: 30
|
||||
});
|
||||
|
||||
// Codes are single-use per period (server side), and the same user may be
|
||||
// logging in from another browser project concurrently.
|
||||
// So, if a code is rejected, wait for the next period and try again.
|
||||
let lastToken = '';
|
||||
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
while (totp.generate() == lastToken) {
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
lastToken = totp.generate();
|
||||
|
||||
await codeInput.fill(lastToken);
|
||||
await page.getByRole('button', { name: 'Log in' }).click();
|
||||
|
||||
const loggedIn = await page
|
||||
.getByRole('button', { name: 'navigation-menu' })
|
||||
.waitFor({ timeout: 5000 })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (loggedIn) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await page.getByRole('tab', { name: 'Dashboard' }).waitFor();
|
||||
await page.getByRole('button', { name: 'navigation-menu' }).waitFor();
|
||||
await page.waitForURL(/\/web(\/home)?/);
|
||||
});
|
||||
Reference in new issue
Block a user