Shuffle/e2e/tests/api/krit08-reset-link.spec.ts
cschlaefke f302c56904 test(auth): add Playwright project with api tests for KTO-01 (GoTrue, Mailpit, db helper)
Refs #38

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 10:29:18 +01:00

106 lines
4.8 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { authConfig, errorCodes } from '../../support/auth-config';
import { expireConfirmationLink, expireRecoveryLink } from '../../support/db';
import { GoTrueClient, randomEmail, randomPassword, waitMailInterval } from '../../support/gotrue';
import { expectMailCount, extractTokenHash, waitForMail } from '../../support/mailpit';
const gotrue = new GoTrueClient();
async function confirmedUser() {
const email = randomEmail();
const password = randomPassword();
const created = await gotrue.createConfirmedUser(email, password);
expect(created.status).toBe(200);
return { email, password };
}
test.describe('KTO-01 Kriterium 8: Passwort-Reset-Link (api)', () => {
test('KTO-01 Kriterium 8: Reset anfordern -> Mail mit type=recovery -> einloesen ok -> zweites Einloesen 403 otp_expired', async () => {
const { email } = await confirmedUser();
expect((await gotrue.recover(email)).status).toBe(200);
const { tokenHash, type } = extractTokenHash(await waitForMail(email));
expect(type).toBe('recovery');
const first = await gotrue.verify(tokenHash, 'recovery');
expect(first.status).toBe(200);
expect(first.body.access_token).toBeTruthy();
const second = await gotrue.verify(tokenHash, 'recovery');
expect(second.status).toBe(403);
expect(second.body.error_code).toBe(errorCodes.otpExpired);
});
test('KTO-01 Kriterium 8: neue Reset-Anforderung ersetzt den alten Link', async () => {
const { email } = await confirmedUser();
await gotrue.recover(email);
const oldLink = extractTokenHash(await waitForMail(email));
await waitMailInterval();
await gotrue.recover(email);
const newLink = extractTokenHash(await waitForMail(email, 2));
expect(newLink.tokenHash).not.toBe(oldLink.tokenHash);
const old = await gotrue.verify(oldLink.tokenHash, 'recovery');
expect(old.status).toBe(403);
expect(old.body.error_code).toBe(errorCodes.otpExpired);
expect((await gotrue.verify(newLink.tokenHash, 'recovery')).status).toBe(200);
});
test('KTO-01 Kriterium 8: AUTH-CFG-07 Reset-Link ist nach 1 h + 1 s abgelaufen (recovery_sent_at zurueckgesetzt)', async () => {
const { email } = await confirmedUser();
await gotrue.recover(email);
const { tokenHash } = extractTokenHash(await waitForMail(email));
await expireRecoveryLink(email);
const res = await gotrue.verify(tokenHash, 'recovery');
expect(res.status).toBe(403);
expect(res.body.error_code).toBe(errorCodes.otpExpired);
});
test('KTO-01 Kriterium 8: AUTH-CFG-07 Grenze: Reset-Link kurz vor Ablauf ist noch gueltig', async () => {
const { email } = await confirmedUser();
await gotrue.recover(email);
const { tokenHash } = extractTokenHash(await waitForMail(email));
await expireRecoveryLink(email, authConfig.otpExpirySeconds - 60);
expect((await gotrue.verify(tokenHash, 'recovery')).status).toBe(200);
});
test('KTO-01 Kriterium 8: AUTH-CFG-07 Bestaetigungslink ist nach 1 h + 1 s abgelaufen (confirmation_sent_at zurueckgesetzt)', async () => {
const email = randomEmail();
const password = randomPassword();
await gotrue.signUp(email, password);
const { tokenHash } = extractTokenHash(await waitForMail(email));
await expireConfirmationLink(email);
const res = await gotrue.verify(tokenHash, 'email');
expect(res.status).toBe(403);
expect(res.body.error_code).toBe(errorCodes.otpExpired);
// Konto bleibt unbestaetigt.
expect((await gotrue.signIn(email, password)).body.error_code).toBe(errorCodes.emailNotConfirmed);
});
test('KTO-01 Kriterium 8: Bestaetigungslink ist einmalig', async () => {
const email = randomEmail();
await gotrue.signUp(email, randomPassword());
const { tokenHash } = extractTokenHash(await waitForMail(email));
expect((await gotrue.verify(tokenHash, 'email')).status).toBe(200);
const again = await gotrue.verify(tokenHash, 'email');
expect(again.status).toBe(403);
expect(again.body.error_code).toBe(errorCodes.otpExpired);
});
test('KTO-01 Kriterium 8: Reset fuer unbekannte Adresse -> 200, keine Mail', async () => {
const unknown = randomEmail('unknown');
expect((await gotrue.recover(unknown)).status).toBe(200);
await expectMailCount(unknown, 0);
});
test('KTO-01 Kriterium 8: nach dem Setzen des neuen Passworts ist der Reset-Link verbraucht', async () => {
const { email } = await confirmedUser();
await gotrue.recover(email);
const { tokenHash } = extractTokenHash(await waitForMail(email));
const session = await gotrue.verify(tokenHash, 'recovery');
const update = await gotrue.updateUser(session.body.access_token, { password: randomPassword() });
expect(update.status).toBe(200);
expect((await gotrue.verify(tokenHash, 'recovery')).body.error_code).toBe(errorCodes.otpExpired);
});
});