Anmeldung ueber Authentik und andere OIDC-Anbieter

Anmeldefluss mit PKCE (S256), State und Nonce. Das ID-Token wird gegen den
JWKS des Anbieters geprueft — Signatur, Aussteller, Empfaenger, Ablauf und
Nonce; alles Noetige kommt aus dem Discovery-Dokument, damit die Anbindung
nicht auf Authentik festgelegt ist.

Wiedererkannt wird jemand an der Kennung des Anbieters (sub), nicht an der
E-Mail-Adresse — Adressen aendern sich. Ein bestehendes Konto mit derselben
Adresse wird verknuepft statt verdoppelt, aber nur bei bestaetigter Adresse:
sonst koennte sich beim Anbieter jemand eine fremde Adresse eintragen und
damit ein hiesiges Konto uebernehmen.

Uebernommen wird bewusst nur, wer jemand ist — plus die Instanz-Administration,
wenn OIDC_ADMIN_GROUP gesetzt ist. Gruppen und Rollen bleiben Sache dieser
Anwendung: die Gruppen im Verzeichnis bedeuten etwas anderes als die hier.

Konten aus dem Verzeichnis haben kein Passwort mehr (passwordHash ist jetzt
optional); Konto-Bereich und Passwort-Anmeldung sagen das statt an einer
leeren Pruefung zu scheitern.

Anleitung mit allen Feldern in Authentik: docs/authentik.md
This commit is contained in:
Florian Wilde
2026-08-15 00:12:21 +02:00
parent e296b1e6c2
commit c8d60d8006
14 changed files with 1039 additions and 11 deletions
+16
View File
@@ -140,6 +140,15 @@ async function handleChangePassword(
throw new QrRequestError("Dieses Konto existiert nicht mehr.", 404);
}
// Konten aus dem externen Verzeichnis haben hier kein Passwort — das
// wird beim Anbieter gewechselt, nicht hier.
if (!user.passwordHash) {
throw new QrRequestError(
"Dieses Konto meldet sich über den externen Anbieter an. Das Passwort wird dort geändert.",
400,
);
}
if (!(await verifyPassword(input.currentPassword, user.passwordHash))) {
throw new QrRequestError("Das bisherige Passwort stimmt nicht.", 403);
}
@@ -227,6 +236,13 @@ async function handleLogin(
);
}
if (!user.passwordHash) {
throw new QrRequestError(
"Dieses Konto meldet sich über den externen Anbieter an. Bitte den Knopf oben benutzen.",
403,
);
}
if (!(await verifyPassword(input.password, user.passwordHash))) {
const failedLogins = user.failedLogins + 1;
await prisma.user.update({