From 75e2f8472626a9e3f832e57424abb0996f962c56 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 19:02:08 +0100 Subject: [PATCH] Redone Auth --- src/plugins/auth.ts | 96 +++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index fc2642c..5de9635 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -1,55 +1,91 @@ import { FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import jwt from "jsonwebtoken"; -import {secrets} from "../utils/secrets"; +import { secrets } from "../utils/secrets.js"; export default fp(async (server: FastifyInstance) => { server.addHook("preHandler", async (req, reply) => { + // 1️⃣ Token holen (Header oder Cookie) + const cookieToken = req.cookies?.token; + const authHeader = req.headers.authorization; + const headerToken = authHeader?.startsWith("Bearer ") + ? authHeader.slice(7) + : null; + + const token = + headerToken && headerToken.length > 10 ? headerToken : cookieToken || null; + + if (!token) { + return reply.code(401).send({ error: "Authentication required" }); + } try { - // 1) Token aus Cookie lesen - const cookieToken = req.cookies?.token - - // 2) Token aus Header lesen (falls Cookie nicht da ist) - const authHeader = req.headers.authorization - const headerToken = authHeader?.startsWith("Bearer ") - ? authHeader.slice(7) - : null - - let token = null - - if(headerToken !== null && headerToken.length > 10){ - token = headerToken - } else if(cookieToken ){ - token = cookieToken - } - - if (!token) { - return // keine Exception → Route darf z. B. public sein - } - - + // 2️⃣ JWT verifizieren const payload = jwt.verify(token, secrets.JWT_SECRET!) as { user_id: string; email: string; - tenant_id?: string; - role?: string; + tenant_id: number; }; - (req as any).user = payload; + if (!payload?.user_id || !payload.tenant_id) { + return reply.code(401).send({ error: "Invalid token" }); + } + + req.user = payload; + + // 3️⃣ Rolle des Nutzers im Tenant laden + const { data: roleData, error: roleError } = await server.supabase + .from("auth_user_roles") + .select("role_id") + .eq("user_id", payload.user_id) + .eq("tenant_id", payload.tenant_id) + .maybeSingle(); + + if (roleError) { + server.log.error("Error fetching user role", roleError); + return reply.code(500).send({ error: "Failed to load user role" }); + } + + if (!roleData) { + return reply.code(403).send({ error: "No role assigned for this tenant" }); + } + + const roleId = roleData.role_id; + + // 4️⃣ Berechtigungen der Rolle laden + const { data: permissions, error: permsError } = await server.supabase + .from("auth_role_permissions") + .select("permission") + .eq("role_id", roleId); + + if (permsError) { + server.log.error("Failed to load permissions", permsError); + return reply.code(500).send({ error: "Permission lookup failed" }); + } + + const perms = permissions?.map((p) => p.permission) ?? []; + + // 5️⃣ An Request hängen + req.role = roleId; + req.permissions = perms; + req.hasPermission = (perm: string) => perms.includes(perm); + } catch (err) { return reply.code(401).send({ error: "Invalid or expired token" }); } }); }); +// 🧩 Fastify Type Declarations declare module "fastify" { interface FastifyRequest { - user?: { + user: { user_id: string; email: string; - tenant_id?: number; - role?: string; + tenant_id: number; }; + role: string; + permissions: string[]; + hasPermission: (permission: string) => boolean; } -} \ No newline at end of file +}