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>
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
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>
|
|
);
|
|
}
|