Files
sshkeyvault/src/app/anmelden/page.tsx
T
florian.wildeandClaude Opus 5.5 4219701b29 feat(anmeldung): lokale Benutzerkonten mit Pflicht-TOTP (#035)
Ohne authentik gab es nur den Notfall-Admin – ein geteiltes Konto ohne
Rollen und ohne erkennbare Akteure im Audit-Log. Admins legen jetzt
lokale Konten an; sshkv erzeugt ein Einmal-Passwort, das bei der ersten
Anmeldung ersetzt wird, danach ist TOTP Pflicht.

Rollen, Sperren (beendet Sitzungen sofort) und Zurücksetzen unter
Einstellungen → Benutzer, nur für Admins mit frischer Bestätigung.
SSHKV_LOKALE_KONTEN=0 schaltet lokale Konten ab; der Notfall-Admin
bleibt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:36:25 +02:00

75 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
import { redirect } from "next/navigation";
import { AnmeldeRahmen } from "@/components/anmeldung/AnmeldeRahmen";
import { KontoFormular } from "@/components/anmeldung/KontoFormular";
import { buttonKlassen, Formularfehler } from "@/components/ui";
import { lokaleKontenAktiv } from "@/lib/lokale-konten";
import { sicheresZiel } from "@/server/auth/anmeldung";
import { oidcAktiv, oidcName } from "@/server/auth/oidc";
import { angemeldet } from "@/server/auth/sitzungspflicht";
export const metadata: Metadata = { title: "Anmelden" };
/** Codes aus /api/auth/oidc/rueckruf – nie der Meldungstext selbst in der Adresse. */
const FEHLER: Record<string, string> = {
"oidc-aus": "Die Anmeldung über den Identitätsanbieter ist nicht eingerichtet.",
"oidc-start": "Der Identitätsanbieter ist nicht erreichbar. Bitte später erneut versuchen.",
"oidc-rueckruf": "Die Anmeldung beim Identitätsanbieter ist fehlgeschlagen. Bitte erneut versuchen.",
"keine-gruppe":
"Ihr Konto ist keiner SSHKeyVault-Gruppe zugeordnet. Bitte beim Administrator Zugriff beantragen.",
"konto-konflikt":
"Für diese E-Mail-Adresse besteht bereits ein anderes Konto. Bitte den Administrator ansprechen.",
deaktiviert: "Dieses Konto ist deaktiviert.",
"fremdes-konto": "Die Bestätigung kam von einem anderen Konto als dem angemeldeten.",
allgemein: "Die Anmeldung ist fehlgeschlagen.",
};
export default async function AnmeldenSeite({
searchParams,
}: {
searchParams: Promise<{ ziel?: string; fehler?: string; abgemeldet?: string }>;
}) {
const parameter = await searchParams;
const ziel = sicheresZiel(parameter.ziel);
if (await angemeldet()) redirect(ziel);
const oidc = oidcAktiv();
const lokal = lokaleKontenAktiv();
const fehler = parameter.fehler ? (FEHLER[parameter.fehler] ?? FEHLER["allgemein"]) : undefined;
return (
<AnmeldeRahmen
titel="Anmelden"
text={parameter.abgemeldet ? "Sie wurden abgemeldet." : "Zentrale Verwaltung von SSH-Schlüsseln."}
>
<div className="flex flex-col gap-5">
<Formularfehler text={fehler} />
{oidc ? (
// Ein echter Link statt next/link: die Route leitet zum Anbieter weiter, ein
// Vorladen durch den Router wuerde einen Anmeldefluss anstossen.
<a
href={`/api/auth/oidc/start?ziel=${encodeURIComponent(ziel)}`}
className={buttonKlassen("primaer", false, "w-full")}
>
Mit {oidcName()} anmelden
</a>
) : (
<p className="text-muted text-[14px]">
{lokal
? "Kein Identitätsanbieter eingerichtet – Anmeldung mit lokalem Konto."
: "Kein Identitätsanbieter eingerichtet – nur der Notfallzugang ist aktiv."}
</p>
)}
<details className="group border-border rounded-box border" open={!oidc}>
<summary className="text-muted hover:text-text flex min-h-11 cursor-pointer items-center px-4 text-[14px] font-medium">
{lokal ? "Lokales Konto oder Notfallzugang" : "Notfallzugang"}
</summary>
<div className="border-border border-t p-4">
<KontoFormular ziel={ziel} lokal={lokal} />
</div>
</details>
</div>
</AnmeldeRahmen>
);
}