Passwort-Anmeldung: Provider, Formulare, drei Zustaende der Shell
This commit is contained in:
@@ -1,15 +1,38 @@
|
||||
"use server";
|
||||
|
||||
import { AuthError } from "next-auth";
|
||||
import { signIn, signOut } from "@/auth";
|
||||
|
||||
// Anmeldung ausschliesslich über Entra ID. Es gibt bewusst keinen
|
||||
// Passwort-Pfad: ein zweiter Anmeldeweg neben dem Firmenkonto hebelt jede
|
||||
// Vorgabe des Mandanten aus — Mehrfaktor, bedingten Zugriff, Sperrung beim
|
||||
// Austritt.
|
||||
// Zwei Anmeldewege, und der zweite ist auf Zeit.
|
||||
//
|
||||
// Die Herkunft muss hier nicht mehr 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.
|
||||
// **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
|
||||
@@ -17,6 +40,47 @@ export async function signInWithEntra() {
|
||||
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<AnmeldeZustand> {
|
||||
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" });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user