Replaces the no-op fastify.authenticate stub and the never-implemented
identity/auth login with real bcrypt password verification, JWT session
issuance/verification (reusing the existing JWT_SECRET), and a Redis-backed
revocation denylist for logout. Adds requireRole('ADMIN') to admin-only
configuration writes across 002-009 that previously relied on a decorator
that never actually checked anything. Adds self-identity (GET /auth/me,
re-validated against live account state) and admin-provisioned accounts
(POST /admin/users).
Making the auth check genuinely reject invalid/missing tokens exposed that
~18 pre-existing integration test files called already-gated routes with no
Authorization header (safe against the old no-op stub, broken against a real
one) — fixed via a shared tests/helpers/auth.ts (loginAs/authHeader) and a
file-by-file pass, plus two related SLA-run cleanup races exposed once admin
setup calls in those files' own beforeAll blocks started actually succeeding.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
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. Rather than depend on
|
|
* prisma/seed/roles.seed.ts having already been run against whatever database the suite
|
|
* connects to, this upserts its own throwaway admin/agent account directly (idempotent — safe
|
|
* to call from many test files' own beforeAll against the same database) and logs in as it.
|
|
*/
|
|
export async function loginAs(
|
|
app: FastifyInstance,
|
|
role: 'ADMIN' | 'AGENT' = 'ADMIN',
|
|
): Promise<string> {
|
|
const email = `test-${role.toLowerCase()}@supporthub.test`;
|
|
await prismaClient.user.upsert({
|
|
where: { email },
|
|
update: {},
|
|
create: {
|
|
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}` };
|
|
}
|