Closes the two gaps 010-identity-auth explicitly deferred (password reset, login rate-limiting), plus a shared password-strength validator both the reset-consume endpoint and admin account creation now depend on. - Password reset: single-use, paired-Redis-key tokens (never in Postgres), identical response regardless of account existence, stubbed delivery via a structured log line (no email infrastructure exists yet). - Password strength: one validatePasswordStrength() call site, wired into both POST /admin/users and the reset-consume flow. - Login rate-limiting: checkRateLimit keyed by submitted email, checked before any credential verification. Also fixes tests/helpers/auth.ts's shared loginAs() helper, which reused two fixed accounts across the whole integration suite via upsert — now rate-limited per email, that collided across ~30 files sharing one budget. Each call now gets a unique email; no call sites needed to change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
import { randomUUID } from 'crypto';
|
|
import { FastifyInstance } from 'fastify';
|
|
import bcrypt from 'bcryptjs';
|
|
import { prismaClient } from '@/infrastructure/database';
|
|
|
|
const TEST_PASSWORD = 'Test-Password-123!';
|
|
|
|
/**
|
|
* 010-identity-auth made fastify.authenticate real — every test file calling a route already
|
|
* gated by it (across 002-009's own suites) needs a real session now. This creates its own
|
|
* throwaway admin/agent account directly and logs in as it, so callers don't depend on
|
|
* prisma/seed/roles.seed.ts having already been run against whatever database the suite
|
|
* connects to.
|
|
*
|
|
* 013-auth-hardening: the email is unique per call (not a fixed `test-admin@...` shared across
|
|
* every integration test file) because login is now rate-limited per email — dozens of files
|
|
* each calling this once in their own beforeAll would otherwise share one rate-limit bucket and
|
|
* trip it well before any file's own tests get to run.
|
|
*/
|
|
export async function loginAs(
|
|
app: FastifyInstance,
|
|
role: 'ADMIN' | 'AGENT' = 'ADMIN',
|
|
): Promise<string> {
|
|
const email = `test-${role.toLowerCase()}-${randomUUID()}@supporthub.test`;
|
|
await prismaClient.user.create({
|
|
data: {
|
|
email,
|
|
name: `Test ${role}`,
|
|
role,
|
|
passwordHash: await bcrypt.hash(TEST_PASSWORD, 10),
|
|
},
|
|
});
|
|
|
|
const response = await app.inject({
|
|
method: 'POST',
|
|
url: '/auth/login',
|
|
payload: { email, password: TEST_PASSWORD },
|
|
});
|
|
return response.json().data.token as string;
|
|
}
|
|
|
|
export function authHeader(token: string): { authorization: string } {
|
|
return { authorization: `Bearer ${token}` };
|
|
}
|