"use server"; import { prisma } from "@/server/db"; import { requirePlatformSession, platformSignOut } from "@/server/platform-auth"; import { verifyTotp, generateRecoveryCodes } from "@/server/mfa"; import { writePlatformAudit } from "@/server/audit"; /** * Plattform-Actions (Phase-1-Härtung Paket 2): MFA-Enrollment und Logout. * EXEMPT vom Modul-Gating (keine per TenantModule gegatete Fachfunktion) — eigene * Autorisierung über requirePlatformSession. */ export type EnrollState = | { status: "idle" } | { status: "error"; message: string } | { status: "done"; recoveryCodes: string[] }; /** * Bestätigt die MFA-Einrichtung: prüft den TOTP-Code gegen das (beim Seitenaufruf * gespeicherte) Einrichtungs-Secret, aktiviert MFA und liefert einmalig die * Recovery-Codes im Klartext zurück (nur Hashes werden gespeichert). */ export async function confirmMfaEnrollment(_prev: EnrollState, formData: FormData): Promise { const session = await requirePlatformSession(); const admin = await prisma.platformAdmin.findUnique({ where: { id: session.user.id } }); if (!admin) return { status: "error", message: "Konto nicht gefunden." }; if (admin.mfaEnrolledAt) return { status: "error", message: "MFA ist bereits eingerichtet." }; if (!admin.mfaSecret) return { status: "error", message: "Kein Einrichtungs-Secret vorhanden. Bitte Seite neu laden." }; const code = String(formData.get("token") ?? ""); if (!verifyTotp(code, admin.mfaSecret)) { return { status: "error", message: "Code ungültig. Bitte den aktuellen 6-stelligen Code aus der App eingeben." }; } const { plain, hashed } = generateRecoveryCodes(10); await prisma.platformAdmin.update({ where: { id: admin.id }, data: { mfaEnrolledAt: new Date(), recoveryCodes: hashed, failedLogins: 0, lockedUntil: null }, }); await writePlatformAudit({ actorId: admin.id, action: "update", entity: "platform_admin", entityId: admin.id, after: { mfaEnrolled: true } }); return { status: "done", recoveryCodes: plain }; } /** Plattform-Logout (eigene Session-Domäne). */ export async function platformSignOutAction() { await platformSignOut({ redirectTo: "/platform/login" }); }