feat(auth): Anmeldung per OIDC mit Rollen aus Gruppen
Authorization Code mit PKCE, State und Nonce über openid-client 6.8.8; die Rolle kommt bei jeder Anmeldung aus dem Gruppen-Claim. Lokale Konten werden nie über den Namen übernommen, verknüpfen geht nur angemeldet. Getestet gegen einen Mock-IdP inklusive gefälschtem State, falscher Nonce und fremder Audience. Closes #016 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Status } from "@/components/status";
|
||||
import { Symbol, Wortmarke } from "@/components/wortmarke";
|
||||
import { oidcEinstellungen } from "@/server/auth/oidc";
|
||||
import { aktuellerBenutzer } from "@/server/auth/zugang";
|
||||
import { Anmeldeformular } from "./anmeldeformular";
|
||||
|
||||
@@ -11,12 +13,25 @@ function sicheresZiel(ziel: string | string[] | undefined): string {
|
||||
return typeof ziel === "string" && ziel.startsWith("/") && !ziel.startsWith("//") ? ziel : "/";
|
||||
}
|
||||
|
||||
const SSO_MELDUNGEN: Record<string, string> = {
|
||||
abgelaufen: "Die SSO-Anmeldung hat zu lange gedauert. Bitte noch einmal.",
|
||||
idp: "Der Identity Provider hat die Anmeldung nicht bestätigt.",
|
||||
keine_gruppe: "Dein SSO-Konto ist für ProxDeploy nicht freigegeben – es fehlt die Gruppe.",
|
||||
gesperrt: "Dein Konto ist gesperrt.",
|
||||
name_vergeben: "Für dein SSO-Konto ließ sich kein Benutzername vergeben.",
|
||||
aus: "Die Anmeldung per SSO ist nicht eingerichtet.",
|
||||
};
|
||||
|
||||
export default async function Anmelden({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<Record<string, string | string[]>>;
|
||||
}) {
|
||||
const ziel = sicheresZiel((await searchParams)["ziel"]);
|
||||
const parameter = await searchParams;
|
||||
const ziel = sicheresZiel(parameter["ziel"]);
|
||||
const sso = oidcEinstellungen();
|
||||
const ssoFehler =
|
||||
typeof parameter["sso"] === "string" ? (SSO_MELDUNGEN[parameter["sso"]] ?? SSO_MELDUNGEN["idp"]) : null;
|
||||
if (await aktuellerBenutzer()) redirect(ziel);
|
||||
|
||||
return (
|
||||
@@ -29,7 +44,27 @@ export default async function Anmelden({
|
||||
</h1>
|
||||
<p className="text-muted text-center text-sm">VMs und LXC-Container auf Proxmox VE ausrollen</p>
|
||||
</div>
|
||||
{ssoFehler ? (
|
||||
<Status art="fehler" className="mb-4 text-sm">
|
||||
{ssoFehler}
|
||||
</Status>
|
||||
) : null}
|
||||
<Anmeldeformular ziel={ziel} />
|
||||
{sso ? (
|
||||
<div className="mt-6 flex flex-col gap-3">
|
||||
<div className="text-muted flex items-center gap-3 text-xs" aria-hidden="true">
|
||||
<span className="bg-linie h-px flex-1" />
|
||||
oder
|
||||
<span className="bg-linie h-px flex-1" />
|
||||
</div>
|
||||
<a
|
||||
href={`/api/v1/auth/oidc/start?ziel=${encodeURIComponent(ziel)}`}
|
||||
className="border-linie bg-panel-hoch text-text hover:border-muted inline-flex h-11 items-center justify-center rounded-lg border px-4 text-sm font-semibold"
|
||||
>
|
||||
Mit {sso.anzeigename} anmelden
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user