import { redirect } from "next/navigation"; import type { Session } from "next-auth"; import { UserRole } from "@prisma/client"; import { auth } from "@/auth"; // ===================================================================== // Auth-Utils für Server Components, Server Actions, Route Handlers // --------------------------------------------------------------------- // Wir kapseln die Session-Auflösung an einer Stelle, damit jede // geschützte Route denselben Sicherheits-Pfad geht: // // 1. `getSession()` → optional, gibt `null` zurück // 2. `requireSession()` → eingeloggt, sonst Redirect /login // 3. `requireKitaSession()` → eingeloggt + kitaId vorhanden + Privacy-Consent // 4. `requireRole()` → zusätzlich Rollen-Whitelist // // Mandanten-Isolation: NIEMALS `session.user.kitaId` ungeprüft an Prisma // reichen. Über `requireKitaSession` ist garantiert, dass der Wert // nicht-null ist und der Consent-Zeitstempel gesetzt wurde. // ===================================================================== export type AuthenticatedSession = Session & { user: NonNullable; }; export type KitaSession = AuthenticatedSession & { user: AuthenticatedSession["user"] & { kitaId: string }; }; export async function getSession(): Promise { return auth(); } export async function requireSession(): Promise { const session = await auth(); if (!session?.user) { redirect("/login"); } return session as AuthenticatedSession; } /** * Garantiert: eingeloggter User MIT Mandantenzuordnung UND akzeptierter * Datenschutzerklärung. Genau diese Funktion ist der Single-Point-of-Truth * für alle Tenant-gebundenen Server Actions / Page Components. */ export async function requireKitaSession(): Promise { const session = await requireSession(); if (!session.user.kitaId) { // Superadmins haben keine Kita → eigene Oberfläche. if (session.user.role === UserRole.SUPERADMIN) { redirect("/admin"); } // Frisch registrierte Gründer → in den Onboarding-Wizard. redirect("/onboarding"); } // DSGVO-Gate: Ohne Privacy-Consent → erst Consent einholen. // Wir prüfen das hier zentral statt in jeder Route einzeln. const consent = await consentCheck(session.user.id); if (!consent) { redirect("/onboarding/consent"); } return session as KitaSession; } export async function requireRole( allowed: UserRole[], ): Promise { const session = await requireSession(); if (!allowed.includes(session.user.role)) { redirect("/forbidden"); } return session; } // --------------------------------------------------------------------- // interne Helfer // --------------------------------------------------------------------- async function consentCheck(userId: string): Promise { // Lazy-Import, damit `auth-utils` selbst Edge-kompatibel bleibt // (Prisma läuft nur in Node-Runtime). const { prisma } = await import("@/lib/prisma"); const user = await prisma.user.findUnique({ where: { id: userId }, select: { privacyPolicyAcceptedAt: true }, }); return !!user?.privacyPolicyAcceptedAt; }