From 181be58a6083f5a7caf0fe4ec704ba3ab70f8ca9 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 4 Mar 2026 14:06:28 +0300 Subject: [PATCH] feat: add auth plugin Made-with: Cursor --- src/plugins/auth.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/plugins/auth.ts diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts new file mode 100644 index 0000000..815f093 --- /dev/null +++ b/src/plugins/auth.ts @@ -0,0 +1,42 @@ +import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; +import fp from 'fastify-plugin'; +import { verifyToken, isAccessPayload } from '../utils/jwt.js'; +import { unauthorized } from '../utils/errors.js'; + +declare module 'fastify' { + interface FastifyInstance { + authenticate: (req: FastifyRequest, reply: FastifyReply) => Promise; + } + interface FastifyRequest { + user?: { id: string; email: string }; + } +} + +export async function authenticate(req: FastifyRequest, _reply: FastifyReply): Promise { + const authHeader = req.headers.authorization; + + if (!authHeader?.startsWith('Bearer ')) { + throw unauthorized('Missing or invalid authorization header'); + } + + const token = authHeader.slice(7); + + try { + const payload = await verifyToken(token); + + if (!isAccessPayload(payload)) { + throw unauthorized('Invalid token type'); + } + + req.user = { id: payload.sub, email: payload.email }; + } catch { + throw unauthorized('Invalid or expired token'); + } +} + +const authPlugin = async (app: FastifyInstance) => { + app.decorateRequest('user', undefined); + app.decorate('authenticate', authenticate); +}; + +export default fp(authPlugin, { name: 'auth' });