diff --git a/web/e2e/mfa.spec.ts b/web/e2e/mfa.spec.ts index 4ac0c47..2ba112a 100644 --- a/web/e2e/mfa.spec.ts +++ b/web/e2e/mfa.spec.ts @@ -135,6 +135,7 @@ test.describe("MFA end-to-end", () => { await page.getByLabel("I have saved these codes").check(); await page.getByRole("button", { name: "Continue" }).click(); await expect(page.getByText("No passkeys registered.")).toHaveCount(0); + await expect(page.getByText(/Added .* ยท Last used/)).toBeVisible(); await page.request.post(`${BASE_URL}/auth/logout`); await fillCredentials(page, email, password); @@ -198,10 +199,11 @@ test.describe("MFA end-to-end", () => { await request.post(`${BASE_URL}/auth/logout`); }); - test("step-up on secret reveal", async ({ page, request }) => { + test("fresh sign-in reveals a secret without a step-up prompt", async ({ page, request }) => { const { email, password } = await createMember(request); - // Enrol TOTP so step-up has a factor to challenge. + // Enrol TOTP so the account has a factor - not exercised in this test, + // since sign-in itself sets StepUpAt and the window has not gone stale. await fillCredentials(page, email, password); await page.goto("/account/security"); await page.getByRole("button", { name: "Set up" }).click(); @@ -212,26 +214,23 @@ test.describe("MFA end-to-end", () => { await page.getByLabel("I have saved these codes").check(); await page.getByRole("button", { name: "Continue" }).click(); - // A brand-new session's StepUpAt is fresh from sign-in, so create a secret, - // then create a group via the UI and attempt a reveal - the modal should - // still appear because sign-in only counts as step-up for ten minutes and - // this test does not wait that long; it is testing the prompt fires the - // first time an authenticated caller with no fresh step-up reveals one. + // Sign-in counts as a step-up (mintSession sets StepUpAt), and that is + // valid for ten minutes - a browser test cannot wait that long or age a + // session, so this only asserts the fresh-session path: no prompt on the + // first reveal, and none on a second reveal right after. The stale path + // (StepUpAt older than ten minutes) is covered by TestStepUpFresh in + // server/internal/auth/stepup_test.go. await request.post(`${BASE_URL}/api/secrets`, { data: { group: "e2e-stepup", values: { KEY: "value" } } }); await page.goto("/secrets/e2e-stepup"); await page.getByRole("button", { name: "Reveal" }).click(); - await expect(page.getByText("Confirm it's you")).toBeVisible(); - await page.getByLabel("6-digit code").fill(authenticator.generate(secret.trim())); - await page.getByRole("button", { name: "Confirm" }).click(); - await expect(page.getByText("Confirm it's you")).toHaveCount(0); await expect(page.locator("span.font-mono.text-xs.break-all")).toBeVisible(); - // Reveal again within the ten-minute step-up window: no prompt this time. await page.getByRole("button", { name: "Hide" }).click().catch(() => {}); await page.getByRole("button", { name: "Reveal" }).click(); await expect(page.getByText("Confirm it's you")).toHaveCount(0); + await expect(page.locator("span.font-mono.text-xs.break-all")).toBeVisible(); }); });