"use server"; import { AuthError } from "next-auth"; import { signIn, signOut } from "@/auth"; // Zwei Anmeldewege, und der zweite ist auf Zeit. // // **Entra ID ist der Hauptweg.** Was der Mandant vorgibt — Mehrfaktor, // bedingter Zugriff, Sperrung beim Austritt — gilt nur auf diesem Weg. Ein // zweiter Weg daneben hebelt all das aus, und deshalb stand hier bis zum // 08.09.2026, dass es ihn bewusst nicht gibt. // // **Warum es ihn jetzt trotzdem gibt.** Fünf Mitarbeiterinnen des Kunden // (@manner.com) sollen die Anwendung testen. Manner hat einen eigenen // Entra-Mandanten; eine Gasteinladung müsste deren IT einrichten und dauert // Wochen. Die Daten im System sind zu diesem Zeitpunkt synthetisch. // // **Was den Weg begrenzt** — nichts davon steht hier, alles in der Datenbank // (Migration 20260908120000), weil eine Regel im Anwendungscode die Regel // wäre, die sich umgehen lässt: // // • Zwang zum Wechsel beim ersten Mal; solange er aussteht, liefert // is_hr_user() false und damit gibt keine einzige Policy eine Zeile her // • sieben Tage Frist für einen nie benutzten Zugang // • Sperre für 15 Minuten nach fünf Fehlversuchen // • Enddatum 08.10.2026, als Prüfbedingung festgenagelt // (chk_passwort_pfad_endet) — danach nimmt die Anmeldung kein Passwort // mehr an, ohne dass sich jemand erinnern muss // // **Vor den echten Manner-Daten muss der Passwortpfad weg sein.** Das ist die // Bedingung, unter der er entstanden ist, nicht eine Empfehlung. // // Die Herkunft muss hier nicht aus dem Request geholt werden: Auth.js baut die // Rückruf-Adresse selbst und akzeptiert nur Ziele auf demselben Host. Ein // untergeschobener Host läuft also weiterhin ins Leere. export async function signInWithEntra() { // Kehrt nicht zurück: signIn löst eine Weiterleitung aus, und die wirft in // Next.js. await signIn("microsoft-entra-id", { redirectTo: "/" }); } export type AnmeldeZustand = { fehler: string | null }; /** * Anmeldung mit E-Mail und Passwort. * * Es gibt **eine** Fehlermeldung für jeden Fehlschlag. Ob die Adresse * unbekannt, das Passwort falsch, das Konto gesperrt oder die Frist abgelaufen * ist, steht im Protokoll und nicht auf dem Bildschirm: das Anmeldeformular * steht offen im Internet, und eine Meldung, die zwischen „gibt es nicht" und * „falsches Passwort" unterscheidet, ist ein Verzeichnis der Belegschaft. * app_passwort_pruefen() nennt aus demselben Grund keinen Grund. */ export async function anmeldenMitPasswort( _zustand: AnmeldeZustand, formular: FormData ): Promise { const email = String(formular.get("email") ?? "").trim(); const passwort = String(formular.get("passwort") ?? ""); if (!email || !passwort) { return { fehler: "Bitte E-Mail-Adresse und Passwort eingeben." }; } try { await signIn("passwort", { email, passwort, redirectTo: "/" }); } catch (fehler) { // Bei Erfolg wirft signIn die Weiterleitung von Next.js. Die darf hier // **nicht** hängenbleiben, sonst endet die Anmeldung auf der Anmeldeseite, // obwohl das Cookie längst gesetzt ist. Nur ein AuthError ist ein // Fehlschlag; alles andere gehört weitergereicht. if (fehler instanceof AuthError) { return { fehler: "E-Mail-Adresse oder Passwort stimmt nicht." }; } throw fehler; } // Unerreichbar — signIn kehrt bei Erfolg nicht zurück. Steht da, weil der // Typprüfer einen Rückgabewert auf jedem Pfad verlangt. return { fehler: null }; } export async function logout() { await signOut({ redirectTo: "/login" }); }