Files
flat-pilot/docs/hauspilot-auth-integration.md
Bastian Wagner e62673ac11 mvp
2026-07-20 09:01:36 +02:00

6.7 KiB

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.