import { expect, test } from '@playwright/test'; import { GoTrueClient, randomEmail, randomPassword } from '../../support/gotrue'; import { extractTokenHash, waitForMail } from '../../support/mailpit'; import { enableSemantics, expectSignedIn, expectSignedInViaLoginRedirect, login, readAuthStorage, shiftBrowserClock } from '../../support/web'; const gotrue = new GoTrueClient(); test.describe('KTO-01 Kriterium 9 (web): Passwort-Reset beendet andere Sitzungen', () => { test('KTO-01 Kriterium 9: zweiter Browser-Kontext ist nach dem Reset und dem naechsten (erzwungenen) Refresh abgemeldet', async ({ page, browser, }) => { const email = randomEmail(); const oldPassword = randomPassword(); await gotrue.createConfirmedUser(email, oldPassword); // Sitzung B: ein zweites Geraet in eigenem Kontext. await login(page, email, oldPassword); await expectSignedIn(page); // Reset ueber Sitzung A (API): neues Passwort setzen beendet alle anderen Sitzungen. await gotrue.recover(email); const { tokenHash } = extractTokenHash(await waitForMail(email)); const recovery = await gotrue.verify(tokenHash, 'recovery'); const update = await gotrue.updateUser(recovery.body.access_token, { password: randomPassword() }); expect(update.status).toBe(200); // Der Access-Token laeuft erst nach jwt_expiry (AUTH-CFG-14, bis 60 min, F1) ab: Der Test // erzwingt den Refresh, indem er die Browser-Uhr um 2 h vorstellt. await shiftBrowserClock(page, 2 * 60 * 60 * 1000); await page.reload(); await enableSemantics(page); // Der Refresh scheitert (refresh_token_not_found): supabase_flutter loescht die Sitzung. await expect.poll(async () => Object.keys(await readAuthStorage(page)), { timeout: 15_000 }).toEqual([]); // Abgemeldet: /login leitet nicht mehr auf die Startseite (Gegenprobe zu Krit. 5). await page.goto('/login'); await enableSemantics(page); await page.waitForTimeout(1_000); await expect(page).toHaveURL(/\/login$/); await expect(page.getByRole('button', { name: 'Sign in', exact: true })).toBeVisible(); }); test('KTO-01 Kriterium 9: Gegenprobe ohne Reset und mit abgelaufenem Access-Token bleibt die Sitzung bestehen (Refresh klappt)', async ({ page, }) => { const email = randomEmail(); const password = randomPassword(); await gotrue.createConfirmedUser(email, password); await login(page, email, password); await expectSignedIn(page); await shiftBrowserClock(page, 2 * 60 * 60 * 1000); const before = JSON.parse(Object.values(await readAuthStorage(page))[0]).access_token; await page.reload(); await expectSignedInViaLoginRedirect(page); // Der Refresh lief wirklich: neues Access-Token im Storage. await expect .poll(async () => JSON.parse(Object.values(await readAuthStorage(page))[0] ?? '{}').access_token) .not.toBe(before); }); });