feat(auth): Anmeldeseiten, TOTP-Einrichtung und Bestaetigung (#004)
Anmeldung, Einrichtung des zweiten Faktors fuer den Notfall-Admin, erneute Bestaetigung vor kritischen Aktionen und die Seite fuer fehlende Rechte. Die Huelle zeigt jetzt den echten Benutzer, und jede Seite prueft die Sitzung selbst; ein struktureller Test haelt das fest. Die Ablaeufe sind im gebauten Stand gegen den Mock-OIDC geprueft (pnpm test:e2e): Admin, Konto ohne Gruppe, Notfall-Admin mit TOTP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import QRCode from "qrcode";
|
||||
import { AnmeldeRahmen } from "@/components/anmeldung/AnmeldeRahmen";
|
||||
import { TotpFormular } from "@/components/anmeldung/TotpFormular";
|
||||
import { MonoKopieren } from "@/components/ui";
|
||||
import { totpAdresse } from "@/lib/totp";
|
||||
import { totpEinrichtungLesen } from "@/server/auth/totp-einrichtung";
|
||||
|
||||
export const metadata: Metadata = { title: "TOTP einrichten" };
|
||||
|
||||
/**
|
||||
* Erste Anmeldung des Notfall-Admins: TOTP ist Pflicht (Freigabe Frage 5). Die Seite ist
|
||||
* nur mit dem Einrichtungs-Cookie erreichbar, das ein richtiges Passwort ausgestellt hat.
|
||||
*/
|
||||
export default async function TotpSeite() {
|
||||
const einrichtung = await totpEinrichtungLesen();
|
||||
if (!einrichtung) redirect("/anmelden");
|
||||
|
||||
const adresse = totpAdresse(einrichtung.geheimnis, "Notfall-Admin");
|
||||
// Als SVG im Server gerendert: kein Bild-Endpunkt, keine Bibliothek im Browser.
|
||||
const svg = await QRCode.toString(adresse, {
|
||||
type: "svg",
|
||||
margin: 1,
|
||||
color: { dark: "#0D1013", light: "#E8ECEF" },
|
||||
});
|
||||
|
||||
return (
|
||||
<AnmeldeRahmen
|
||||
titel="Zweiten Faktor einrichten"
|
||||
text="Code mit einer Authenticator-App scannen und den angezeigten Code eingeben."
|
||||
>
|
||||
<div className="flex flex-col gap-5">
|
||||
<div
|
||||
className="mx-auto w-[200px] overflow-hidden rounded-[10px]"
|
||||
aria-label="QR-Code für die Authenticator-App"
|
||||
role="img"
|
||||
dangerouslySetInnerHTML={{ __html: svg }}
|
||||
/>
|
||||
<MonoKopieren wert={einrichtung.geheimnis} beschriftung="Geheimnis" umbrechen />
|
||||
<TotpFormular />
|
||||
</div>
|
||||
</AnmeldeRahmen>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user