generated from bastian/boilerplate
mvp
This commit is contained in:
123
docs/hauspilot-auth-integration.md
Normal file
123
docs/hauspilot-auth-integration.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# HausPilot: Authentifizierungs- und Benutzerintegration
|
||||
|
||||
## Ergebnis der Boilerplate-Analyse
|
||||
|
||||
HausPilot verwendet unveraendert die vorhandene Backend-for-Frontend-
|
||||
Authentifizierung. Das Boilerplate spricht keinen bestimmten Hersteller fest,
|
||||
sondern einen ueber `OIDC_ISSUER` konfigurierten OpenID-Connect-Provider. Der
|
||||
Login ist ein OIDC Authorization Code Flow mit PKCE, `state` und `nonce`.
|
||||
|
||||
Der Browser startet den Login ueber `GET /api/auth/login`. Der Callback liegt
|
||||
fest unter `<APP_BASE_URL>/api/auth/callback`. Der Backend-Callback validiert
|
||||
Issuer, Audience, Signaturalgorithmus, Nonce und Subject, laedt gegebenenfalls
|
||||
UserInfo und legt den lokalen Benutzer an oder aktualisiert ihn. Die stabile
|
||||
Identitaet beim Provider ist `(issuer, subject)`; fachliche Beziehungen verwenden
|
||||
ausschliesslich die interne UUID `users.id`.
|
||||
|
||||
OIDC Access-, Refresh- und ID-Tokens werden verschluesselt in der MySQL-Tabelle
|
||||
`sessions` gespeichert. Der Browser erhaelt nur eine signierte, HttpOnly
|
||||
Session-ID und ein lesbares CSRF-Cookie. Schreibende Requests senden dieses
|
||||
Cookie ueber den vorhandenen `csrfInterceptor` als `X-CSRF-Token`. Idle- und
|
||||
Absolute-Timeout werden beim serverseitigen Session-Resolve geprueft. Es gibt
|
||||
keinen zweiten Token-Refresh im Browser.
|
||||
|
||||
Der globale `PermissionsGuard` loest die Session auf, prueft den globalen
|
||||
Benutzerstatus und setzt `AuthenticatedRequest.user` mit interner UUID,
|
||||
Session-ID und effektiven Permissions. Controller verwenden weiterhin
|
||||
`@RequirePermissions(...)`. Deaktivierte Benutzer werden beim Login und bei
|
||||
jeder Session-Aufloesung abgewiesen; ihre historischen fachlichen Beziehungen
|
||||
werden nicht geloescht.
|
||||
|
||||
Angular ermittelt den aktuellen Benutzer ueber `AuthService.ensureLoaded()` und
|
||||
`GET /api/me`. Der Auth-State liegt ausschliesslich in dessen Signals. Der
|
||||
vorhandene `permissionGuard` steuert nur Navigation und Darstellung; das Backend
|
||||
bleibt verbindlich. Logout erfolgt ausschliesslich ueber `/api/auth/logout`,
|
||||
widerruft die lokale Session, loescht Session- und CSRF-Cookie und verwendet
|
||||
danach den OIDC `end_session_endpoint` beziehungsweise `OIDC_LOGOUT_URL`.
|
||||
|
||||
## Registrierung und Benutzerprofil
|
||||
|
||||
Das Boilerplate besitzt keinen lokalen Registrierungs- oder Passwortdialog und
|
||||
keine lokale Passwort-Entity. Konten werden durch den vorhandenen Identity
|
||||
Provider registriert oder bereitgestellt und beim ersten erfolgreichen OIDC-
|
||||
Login lokal synchronisiert. HausPilot fuehrt daher keine zweite Registrierung
|
||||
ein. Nach dem ersten Login werden offene Einladungen nur angezeigt, niemals
|
||||
automatisch angenommen.
|
||||
|
||||
Die lokale `UserEntity` speichert UUID, Issuer, Subject, Anzeigename, E-Mail,
|
||||
Aktivstatus, globale Rollen und Einstellungen. Name und E-Mail werden bei jedem
|
||||
Login aus dem OIDC-Profil aktualisiert. Aktive Mitgliedschaften bleiben auch bei
|
||||
einer E-Mail-Aenderung ueber `users.id` stabil. Offene Einladungen bleiben an die
|
||||
normalisierte urspruengliche Zieladresse gebunden.
|
||||
|
||||
Die Integration uebernimmt ausserdem den standardisierten OIDC-Claim
|
||||
`email_verified`. Eine Einladung kann nur angenommen werden, wenn der Provider
|
||||
die aktuelle Zieladresse als verifiziert bestaetigt. Fehlt diese Bestaetigung,
|
||||
wird die Annahme sicher abgewiesen.
|
||||
|
||||
## Globale und projektbezogene Autorisierung
|
||||
|
||||
Globale Rollen (`admin`, `user` und verwaltete globale Rollen) bleiben von den
|
||||
HausPilot-Projektrollen getrennt. Eine globale Basis-Permission erlaubt nur die
|
||||
Verwendung der Projekt-API. Sie erzeugt weder eine Mitgliedschaft noch einen
|
||||
Zugriff auf ein privates Projekt. Insbesondere erhaelt ein globaler Administrator
|
||||
keinen impliziten Projektzugriff.
|
||||
|
||||
Projektrollen sind `owner`, `administrator`, `editor` und `reader`. Jede
|
||||
Projektabfrage prueft serverseitig eine aktive Mitgliedschaft und die fuer die
|
||||
Aktion erforderliche Rolle. Unterentitaeten werden immer zusammen mit der
|
||||
Projekt-ID geladen beziehungsweise geprueft. Eigentuermer-, Autor- und
|
||||
Einladender-IDs sind keine DTO-Felder, sondern stammen aus dem vorhandenen
|
||||
serverseitigen Current-User-Kontext.
|
||||
|
||||
Projektanlage, Eigentuemermitgliedschaft und Erstellungsaktivitaet werden in
|
||||
einer Datenbanktransaktion gespeichert. Projektrollen entstehen nur durch diese
|
||||
Projektanlage, explizite Einladungsannahme oder eine berechtigte Aenderung im
|
||||
Projekt; OIDC-Gruppen und globale Rollen werden nicht abgebildet.
|
||||
|
||||
## Einladungen und sicherer Rueckkehrpfad
|
||||
|
||||
Einladungen speichern die normalisierte E-Mail-Adresse und optional die bekannte
|
||||
interne Benutzer-ID. Der zufaellige Einladungstoken wird nur im Link ausgegeben;
|
||||
in MySQL liegt ausschliesslich sein SHA-256-Hash. Annahme und Ablehnung verlangen
|
||||
eine gueltige bestehende Session. Das Backend prueft Status, Ablauf,
|
||||
Widerruf, Zieladresse, `email_verified`, aktiven Benutzer und eine noch nicht
|
||||
bestehende aktive Mitgliedschaft. Fehlermeldungen geben nur eine maskierte
|
||||
Adresse aus.
|
||||
|
||||
Ist ein Besucher nicht angemeldet, verweist Angular auf den bestehenden
|
||||
`/api/auth/login`-Flow. Der gewuenschte Rueckweg wird serverseitig im kurzlebigen
|
||||
OIDC-Login-State gehalten. Erlaubt sind nur interne Pfade; Schemes,
|
||||
Protokoll-relative URLs und Backslashes werden verworfen. Nach dem Callback baut
|
||||
das Backend das Ziel relativ zu `FRONTEND_BASE_URL` auf. Tokens oder OIDC-Daten
|
||||
werden nie in Rueckkehrparametern gespeichert.
|
||||
|
||||
## Benachrichtigungen, Mail und Cleanup
|
||||
|
||||
Das vorhandene `NotificationsService` erzeugt fuer bereits bekannte aktive
|
||||
Benutzer eine persoenliche In-App-Benachrichtigung. Normale Endpunkte bleiben auf
|
||||
den Session-Benutzer beschraenkt. Das Boilerplate hat keinen Mail-Service, keine
|
||||
Templates, Queue oder Retry-Jobs. HausPilot fuehrt keine parallele
|
||||
Mail-Infrastruktur ein; der Versandstatus einer Einladung wird daher explizit
|
||||
als `not_configured` gespeichert. Der Einladungsdatensatz bleibt konsistent und
|
||||
kann spaeter an eine architektonisch beschlossene Mail-Komponente angebunden
|
||||
werden.
|
||||
|
||||
Projektseiten halten Daten nur komponentenlokal. Bei 401 setzt der vorhandene
|
||||
globale Auth-State den Benutzer zurueck; dadurch werden geschuetzte Seiten
|
||||
entfernt und das bestehende Notification-Polling samt personenbezogenem Cache
|
||||
gestoppt. Ein regulaerer Logout verlaesst die SPA und startet sie ohne alten
|
||||
In-Memory-State neu. HausPilot fuehrt keine Live-Verbindungen ein.
|
||||
|
||||
## Relevante Konfiguration
|
||||
|
||||
- OIDC: `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`,
|
||||
`OIDC_SCOPES`, `OIDC_ALLOWED_ALGORITHMS`, optional `OIDC_LOGOUT_URL`
|
||||
- Redirects: `APP_BASE_URL`, `FRONTEND_BASE_URL`
|
||||
- Session: `SESSION_COOKIE_NAME`, Idle-/Absolute-Timeout,
|
||||
`SESSION_SECRET`, `SESSION_ENCRYPTION_KEY`
|
||||
- Browser-Schutz: `CORS_ORIGINS`, `CSRF_HEADER_NAME`, Secure/SameSite-Cookies,
|
||||
Helmet, Rate Limits und Logging-Redaction
|
||||
|
||||
Es wurde keine parallele Authentifizierungs-, Benutzer-, Passwort-, Token-,
|
||||
Registrierungs-, Mail-, Queue- oder Live-Connection-Infrastruktur eingefuehrt.
|
||||
Reference in New Issue
Block a user