feat(auth): OIDC/Keycloak SSO-Login (additiv) — v1.2.91

SSO per OpenID Connect (Authorization Code + PKCE) zusätzlich zum lokalen Login.
- Regeln: kein Auto-Provisioning (E-Mail muss als User existieren), Rolle aus DB (nie aus Token), lokaler Login+TOTP unangetastet.
- Migration 0040: oidc_settings (Singleton, client_secret_enc via secrets.Box) + users.oidc_subject.
- internal/services/oidc: Settings-Repo (write-only Secret) + lazy go-oidc Client (testbarer Authenticator-Seam).
- internal/handlers/oidc.go: GET/PUT /oidc/settings (admin), GET /auth/oidc/{settings,login,callback}. Flow-State (state/PKCE/nonce) stateless im 5-min signierten HttpOnly-Cookie (SameSite=Lax). email_verified erzwungen, opportunistisches sub-Linking, Session via setSessionCookie+Signer.
- session.SignBlob/VerifyBlob; users.Get/SetOIDCSubject; main.go-Wiring.
- Frontend: App.tsx /auth/me-Bootstrap (für Cookie-Session nach Callback), Login-SSO-Button + sso_error, Settings OIDC-Card, i18n de/en.
- Tests (guarded EG_FWTEST_DSN): Secret-Roundtrip + Callback (Rolle-aus-DB, no_account, disabled, unverified, nonce, state).
Deps: go-oidc/v3, x/oauth2. Scope v1: nur Login (kein SLO/Refresh).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Debian
2026-06-05 16:04:13 +02:00
parent f85552a475
commit 3a707e2e3f
18 changed files with 1326 additions and 8 deletions

View File

@@ -289,6 +289,24 @@
"forgotPassword": "Passwort vergessen?",
"viewerBadge": "Nur lesen",
"viewerHint": "Dieser Account hat die Rolle Betrachter — Änderungen sind gesperrt. Ein Admin kann die Rolle anpassen.",
"sso": {
"login": "Mit SSO anmelden",
"err": {
"no_account": "Kein EdgeGuard-Konto für diese E-Mail. Bitte wende dich an einen Administrator.",
"disabled": "Dieses Konto ist deaktiviert.",
"unverified": "Die E-Mail-Adresse ist beim Identity-Provider nicht verifiziert.",
"state": "Sicherheitsprüfung fehlgeschlagen (State). Bitte erneut versuchen.",
"nonce": "Sicherheitsprüfung fehlgeschlagen (Nonce). Bitte erneut versuchen.",
"token": "Das Token vom Identity-Provider konnte nicht verifiziert werden.",
"exchange": "Code-Austausch mit dem Identity-Provider fehlgeschlagen.",
"expired": "Die Anmeldesitzung ist abgelaufen. Bitte erneut versuchen.",
"denied": "Anmeldung beim Identity-Provider abgebrochen.",
"subject_mismatch": "Die Identität passt nicht zum hinterlegten Konto.",
"config": "SSO ist nicht korrekt konfiguriert.",
"server": "Interner Fehler bei der SSO-Anmeldung.",
"generic": "SSO-Anmeldung fehlgeschlagen."
}
},
"totp": {
"prompt": "Bitte gib den 6-stelligen Code aus deiner Authenticator-App ein.",
"verify": "Code bestätigen",
@@ -907,6 +925,24 @@
"configPreviewBtn": "Vorschau laden",
"configPreviewHint": "Rendert die gewählte Service-Config aus dem aktuellen DB-State. Nur Lesezugriff — es wird nichts auf Disk geschrieben oder neu geladen.",
"configCopied": "Config in Zwischenablage kopiert",
"oidc": {
"title": "Single Sign-On (OIDC)",
"intro": "SSO-Login per OpenID Connect (z. B. Keycloak), zusätzlich zum lokalen Login. Anmeldung gelingt nur für bereits angelegte Benutzer; die Rolle wird in EdgeGuard verwaltet.",
"enabled": "SSO aktiviert",
"issuerUrl": "Issuer-URL",
"issuerHint": "Basis-URL des Realms, z. B. https://keycloak.example.com/realms/edgeguard",
"clientId": "Client-ID",
"clientSecret": "Client-Secret",
"secretSet": "Gespeichert — leer lassen, um es unverändert zu lassen.",
"secretUnset": "Noch kein Secret gespeichert.",
"scopes": "Scopes",
"emailClaim": "E-Mail-Claim",
"buttonLabel": "Button-Beschriftung",
"redirectUri": "Redirect-URI",
"redirectHint": "Diese URL im Keycloak-Client als gültige Redirect-URI eintragen (pro Cluster-Node die jeweilige FQDN).",
"saved": "OIDC-Einstellungen gespeichert",
"saveFailed": "Speichern der OIDC-Einstellungen fehlgeschlagen"
},
"passwordCardTitle": "Admin-Passwort ändern",
"currentPassword": "Aktuelles Passwort",
"newPassword": "Neues Passwort",
@@ -1287,6 +1323,7 @@
"common": {
"yes": "Ja",
"no": "Nein",
"or": "oder",
"save": "Speichern",
"cancel": "Abbrechen",
"loading": "Lädt …",