import { FastifyInstance } from "fastify" import fp from "fastify-plugin" import jwt from "jsonwebtoken" import { secrets } from "../utils/secrets" import { authUserRoles, authRolePermissions, authUsers, } from "../../db/schema" import { eq, and } from "drizzle-orm" export default fp(async (server: FastifyInstance) => { server.addHook("preHandler", async (req, reply) => { // 1️⃣ Token aus Header oder Cookie lesen 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 { // 2️⃣ JWT verifizieren const payload = jwt.verify(token, secrets.JWT_SECRET!) as { user_id: string email: string tenant_id: number | null } if (!payload?.user_id) { return reply.code(401).send({ error: "Invalid token" }) } // Payload an Request hängen req.user = payload const [currentUser] = await server.db .select({ is_admin: authUsers.is_admin, }) .from(authUsers) .where(eq(authUsers.id, payload.user_id)) .limit(1) req.user.is_admin = Boolean(currentUser?.is_admin) // Multi-Tenant Modus ohne ausgewählten Tenant → keine Rollenprüfung if (!req.user.tenant_id) { return } const tenantId = req.user.tenant_id const userId = req.user.user_id // -------------------------------------------------------- // 3️⃣ Rolle des Nutzers im Tenant holen // -------------------------------------------------------- const roleRows = await server.db .select() .from(authUserRoles) .where( and( eq(authUserRoles.user_id, userId), eq(authUserRoles.tenant_id, tenantId) ) ) .limit(1) if (roleRows.length === 0) { if (req.user.is_admin) { req.role = "" req.permissions = [] req.hasPermission = () => false return } return reply .code(403) .send({ error: "No role assigned for this tenant" }) } const roleId = roleRows[0].role_id // -------------------------------------------------------- // 4️⃣ Berechtigungen der Rolle laden // -------------------------------------------------------- const permissionRows = await server.db .select() .from(authRolePermissions) .where(eq(authRolePermissions.role_id, roleId)) const permissions = permissionRows.map((p) => p.permission) // -------------------------------------------------------- // 5️⃣ An Request hängen für spätere Nutzung // -------------------------------------------------------- req.role = roleId req.permissions = permissions req.hasPermission = (perm: string) => permissions.includes(perm) } catch (err) { console.error("JWT verification error:", err) return reply.code(401).send({ error: "Invalid or expired token" }) } }) }) // --------------------------------------------------------------------------- // Fastify TypeScript Erweiterungen // --------------------------------------------------------------------------- declare module "fastify" { interface FastifyRequest { user: { user_id: string email: string tenant_id: number | null is_admin?: boolean } role: string permissions: string[] hasPermission: (permission: string) => boolean } }