import fp from 'fastify-plugin'; import fastifyJwt from '@fastify/jwt'; import fastifyCookie from '@fastify/cookie'; import type { FastifyReply } from 'fastify'; import { env } from '../config/env.js'; import { UnauthorizedError } from '../utils/errors.js'; export const AUTH_COOKIE = 'fw_auth'; const AUTH_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; // 7 days export default fp(async (app) => { await app.register(fastifyCookie, { secret: env.COOKIE_SECRET, parseOptions: {}, }); await app.register(fastifyJwt, { secret: env.JWT_SECRET, cookie: { cookieName: AUTH_COOKIE, signed: false }, sign: { expiresIn: `${AUTH_COOKIE_MAX_AGE}s` }, }); app.decorate('authenticate', async (request) => { try { await request.jwtVerify({ onlyCookie: true }); } catch { throw new UnauthorizedError(); } }); // helpers for routes app.decorate('setAuthCookie', ((reply: FastifyReply, token: string) => { reply.setCookie(AUTH_COOKIE, token, { httpOnly: true, secure: env.NODE_ENV === 'production', sameSite: 'lax', path: '/', maxAge: AUTH_COOKIE_MAX_AGE, }); }) as never); app.decorate('clearAuthCookie', ((reply: FastifyReply) => { reply.clearCookie(AUTH_COOKIE, { path: '/' }); }) as never); }); declare module 'fastify' { interface FastifyInstance { setAuthCookie: (reply: import('fastify').FastifyReply, token: string) => void; clearAuthCookie: (reply: import('fastify').FastifyReply) => void; } }