import {test, expect, type Page} from '@playwright/test'; import {apiCreateUser, loginUser, randomString, testUserPassword} from './utils.ts'; const signedIn = /^(?!.*\/user\/(login|webauthn))/; // the target of a finished login varies async function registerKey(page: Page, nickname: string) { await page.goto('/user/settings/security'); await page.getByLabel('Nickname').fill(nickname); await page.getByRole('button', {name: 'Add Security Key'}).click(); } async function signInWithPassword(page: Page, username: string) { await page.context().clearCookies(); await page.goto('/user/login'); await page.getByLabel('Username or Email Address').fill(username); await page.getByLabel('Password').fill(testUserPassword); await page.getByRole('button', {name: 'Sign In'}).click(); } // regression: credProtect level 3 hid the credential from the second-factor login test('security key survives credProtect', async ({page, request, browserName}) => { test.skip(browserName !== 'chromium', 'only the CDP authenticator emulates credProtect'); // eslint-disable-line playwright/no-skipped-test const username = `e2e-credprotect-${randomString(8)}`; await apiCreateUser(request, username); const cdp = await page.context().newCDPSession(page); await cdp.send('WebAuthn.enable'); await cdp.send('WebAuthn.addVirtualAuthenticator', {options: { protocol: 'ctap2', ctap2Version: 'ctap2_1', transport: 'usb', hasResidentKey: true, hasUserVerification: true, hasCredBlob: true, // CDP only emulates credProtect together with credBlob isUserVerified: true, }}); await loginUser(page, username); await registerKey(page, 'e2e-key'); await expect(page.getByText('e2e-key')).toBeVisible(); await registerKey(page, 'e2e-key-again'); await expect(page.locator('#webauthn-error-msg')).toContainText('already registered'); await signInWithPassword(page, username); await expect(page).toHaveURL(signedIn); }); // this authenticator has no credProtect, so it cannot replace the test above test('security key signs in as second factor and as passkey', async ({page, request}) => { const username = `e2e-passkey-${randomString(8)}`; await apiCreateUser(request, username); await page.context().credentials.install(); await loginUser(page, username); await registerKey(page, 'e2e-key'); await expect(page.getByText('e2e-key')).toBeVisible(); await signInWithPassword(page, username); await expect(page).toHaveURL(signedIn); await page.context().clearCookies(); await page.goto('/user/login'); await page.getByText('Sign in with a passkey').click(); await expect(page).toHaveURL(signedIn); });