import { FastifyInstance } from 'fastify'; import fp from 'fastify-plugin'; import { UnauthorizedError, ForbiddenError } from '../utils/errors.js'; import { config } from '../utils/config.js'; import type { JwtPayload } from '../types/auth.js'; // Extend @fastify/jwt's type system for admin JWT declare module '@fastify/jwt' { interface FastifyJWT { payload: JwtPayload; } } async function adminAuthMiddleware(app: FastifyInstance): Promise { app.addHook('onRequest', async (request) => { if (!request.url.startsWith('/v1/admin')) { return; } // Skip public admin endpoints const publicPaths = ['/v1/admin/auth', '/v1/admin/auth/login']; if (publicPaths.some((p) => request.url === p)) { return; } const authHeader = request.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { throw new UnauthorizedError('Missing admin token'); } const token = authHeader.slice(7); // Try JWT verification first (new way) try { const decoded = app.jwt.verify(token); if (decoded.authType === 'admin') { // Successfully verified admin JWT - request.jwtVerify() will attach the decoded payload return; } } catch { // JWT verification failed, try fallback (backward compatibility) } // Fallback: ADMIN_TOKEN (legacy way) if (token === config.ADMIN_TOKEN) { // Manually attach a fake decoded payload for legacy token request.jwtVerify = async () => ({ userId: 'legacy-admin', authType: 'admin', role: 'super_admin', }); return; } throw new ForbiddenError('Invalid admin token'); }); } export default fp(adminAuthMiddleware);