The attribute plugin is not available, 2fa disabled

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
de:int:wvdsshell:notes:01-auth-architecture:auth-gateway [2026/03/05 14:37] – [Gateway.Service — Auth-Erweiterungen für WvdS.Shell] Wolfgang van der Stillede:int:wvdsshell:notes:01-auth-architecture:auth-gateway [Unbekanntes Datum] (aktuell) – gelöscht - Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1
Zeile 1: Zeile 1:
-====== 2. Gateway.Service — Auth-Erweiterungen für WvdS.Shell ====== 
- 
-//Stand: 2026-03-05// 
- 
-Übergeordnet: [[de:int:wvdsshell:notes:01-auth-architecture:start|Auth-Architektur — Gesamtübersicht]] 
-Verwandt: [[de:int:wvdsshell:notes:01-auth-architecture:auth-shell|2. WvdS.Shell]] | [[de:int:wvdsshell:notes:01-auth-architecture:auth-database|4. Datenschicht]] | [[de:int:wvdsshell:notes:01-auth-architecture:auth-migration|5. Migration]] 
- 
-===== Ausgangslage ===== 
- 
-Gateway.Service hat eine vollständige Middleware-Pipeline und ein Auth-Enum mit 
-allen benötigten Modi — jedoch ist nur ein Teil davon implementiert. 
- 
-==== Aktuell implementiert ==== 
- 
-^ Auth-Modus        ^ Status       ^ Anmerkung                                      ^ 
-| ''amNone''        | ✓ Aktiv      | Nur für DEV (''$IFDEF DEBUG''                | 
-| ''amBasic''       | ✓ Aktiv      | ConstantTimeEqual, CWE-208                     | 
-| ''amApiKey''      | ✓ Aktiv      | ConstantTimeEqual, Header konfigurierbar       | 
-| ''amBearer''      | — Reserviert | Interface vorhanden, nicht implementiert       | 
-| ''amCertificate'' | — Reserviert | Interface vorhanden, nicht implementiert       | 
-| ''amWindows''     | — Reserviert | Interface vorhanden, nicht implementiert       | 
- 
-==== Benötigt für WvdS.Shell ==== 
- 
-Für die Shell-Integration müssen folgende Komponenten neu implementiert werden: 
- 
-  - **PFX Install-ID Validierung** (''amCertificate'') 
-  - **Kerberos/SSPI Handler** (''amWindows'') 
-  - **JWT Ausstellung und Validierung** (''amBearer'') 
-  - **TOTP Service** (neu — für externen MFA-Flow) 
-  - **Refresh Token Management** (neu) 
-  - **Feature-Flags Endpoint** (neu — ENIVERSASYS-Anbindung) 
- 
-===== Neue Endpoints ===== 
- 
-Alle neuen Auth-Endpoints unter ''%%/api/v1/auth/%%''. 
-Ein neuer Controller ''WvdS.Data.Gateway.Controller.Auth'' wird benötigt. 
- 
-==== Übersicht ==== 
- 
-^ Verb   ^ Route                          ^ Auth erforderlich               ^ Beschreibung                           ^ 
-| POST   | ''/api/v1/auth/kerberos''      | Nein (ist der Auth-Schritt selbst) | Kerberos Negotiate-Handshake        | 
-| POST   | ''/api/v1/auth/external''      | Nein                            | Username/Passwort → TOTP-Challenge     | 
-| POST   | ''/api/v1/auth/totp/verify''   | Nein (Challenge-ID statt Token) | TOTP-Code einlösen → JWT              | 
-| POST   | ''/api/v1/auth/token/refresh'' | Refresh Token                   | Access Token erneuern                  | 
-| GET    | ''/api/v1/auth/totp/enroll''   | JWT Bearer                      | TOTP Enrollment-URI + QR generieren    | 
-| DELETE | ''/api/v1/auth/totp/enroll''   | JWT Bearer                      | TOTP-Enrollment zurücksetzen           | 
-| GET    | ''/api/v1/features''           | JWT Bearer                      | Feature-Flags für aktuelle Session     | 
- 
-==== POST /api/v1/auth/kerberos ==== 
- 
-Kerberos Negotiate-Handshake (mehrstufig, wie HTTP Negotiate definiert): 
- 
-<code> 
-Request: 
-  POST /api/v1/auth/kerberos 
-  Authorization: Negotiate <Base64-SPNEGO-Token> 
-  X-WvdS-Install-Id: ENIVERS-2026-001 
- 
-Response (ggf. mehrstufig): 
-  HTTP 401  Authorization: Negotiate <Base64-Challenge>   ← Server-Challenge 
-  HTTP 200  { "access_token": "...", "refresh_token": "...", "expires_in": 900 } 
-</code> 
- 
-==== POST /api/v1/auth/external ==== 
- 
-Erster Schritt des externen MFA-Flows. Gibt bei gültigem Username/Passwort 
-eine ''challenge_id'' zurück — noch kein Token. 
- 
-<code json> 
-Request: 
-  { "username": "max.muster", "password": "...", "install_id": "ENIVERS-2026-001" } 
- 
-Response 200: 
-  { "mfa_required": true, "challenge_id": "c7f3a..." } 
- 
-Response 401: 
-  { "error": "invalid_credentials" } 
-</code> 
- 
-Passwort wird nach Validierung sofort via ''FillChar'' zeroiert (CWE-316). 
- 
-==== POST /api/v1/auth/totp/verify ==== 
- 
-Zweiter Schritt des externen MFA-Flows. Löst die ''challenge_id'' gegen 
-den TOTP-Code ein und gibt bei Erfolg die Token-Pair aus. 
- 
-<code json> 
-Request: 
-  { "challenge_id": "c7f3a...", "code": "482910" } 
- 
-Response 200: 
-  { "access_token": "...", "refresh_token": "...", "expires_in": 900 } 
- 
-Response 401: 
-  { "error": "invalid_code", "attempts_remaining": 2 } 
-</code> 
- 
-==== POST /api/v1/auth/token/refresh ==== 
- 
-Silent Token Refresh. Refresh-Token wird im Request-Body oder als 
-''Authorization: Refresh <token>'' übergeben. 
- 
-<code json> 
-Request: 
-  { "refresh_token": "..." } 
- 
-Response 200: 
-  { "access_token": "...", "expires_in": 900 } 
- 
-Response 401: 
-  { "error": "refresh_token_expired" } 
-</code> 
- 
-==== GET /api/v1/features ==== 
- 
-Lädt die Feature-Flags für den authentifizierten User/die Installation. 
-Liest aktuell aus ENIVERSCAFM (''auth.*''-Namespace), zukünftig aus ENIVERSASYS. 
- 
-<code json> 
-Response 200: 
-  { "features": ["grids", "charts", "scheduling", "docking"] } 
-</code> 
- 
-===== JWT — Ausstellung und Validierung ===== 
- 
-==== Signaturalgorithmus ==== 
- 
-JWT wird mit **ML-DSA-65** signiert — die Bibliothek ist im Gateway bereits 
-vorhanden (''WvdS.Security.Cryptography''). Kein externer Abhängigkeitsbedarf. 
- 
-Standard-Header: ''{{"alg": "ML-DSA-65", "typ": "JWT"}}'' 
- 
-==== Payload-Struktur ==== 
- 
-<code json> 
-{ 
-  "sub":        "max.muster@corp.example.com", 
-  "install_id": "ENIVERS-2026-001", 
-  "auth_method": "windows", 
-  "mfa":        false, 
-  "iat":        1741132800, 
-  "exp":        1741133700, 
-  "jti":        "a3f9..." 
-} 
-</code> 
- 
-Feature-Flags werden **nicht** im JWT mitgeführt — sie werden bei Bedarf 
-frisch vom ''/api/v1/features''-Endpoint geladen. Grund: Flags können sich 
-ändern ohne dass ein neues Token ausgestellt werden muss. 
- 
-==== Token-Lebensdauer ==== 
- 
-^ Token          ^ Lebensdauer ^ Speicherort (Client)               ^ 
-| Access Token   | 15 Minuten  | RAM (Shell SessionManager)         | 
-| Refresh Token  | 30 Tage     | DPAPI SecretStorage                | 
-| TOTP Challenge | 5 Minuten   | Gateway-RAM (nach Ablauf gelöscht) | 
- 
-==== Validierung in Middleware ==== 
- 
-''TWvdSGatewayAuthHandler'' wird erweitert: Bei ''amBearer'' wird der JWT-Header 
-extrahiert, ML-DSA-Signatur geprüft, ''exp''-Claim validiert und ''install_id'' 
-gegen die PFX-Registrierung abgeglichen. 
- 
-===== TOTP Service ===== 
- 
-==== Zuständigkeit ==== 
- 
-Die gesamte TOTP-Logik liegt im Gateway — die Shell liefert nur den Code 
-(6 Ziffern). Details: [[de:int:wvdsshell:notes:01-auth-architecture:auth-shell#totp_aufgabenteilung|2. Shell-Doku: TOTP Aufgabenteilung]]. 
- 
-==== Implementierungsdetails ==== 
- 
-**Algorithmus:** HMAC-SHA1 (RFC 6238) über OpenSSL (''WvdS.Security.Cryptography'') — kein neuer Crypto-Code nötig. 
- 
-**Shared Secret Speicherung:** 
-  * Pro User ein Base32-kodiertes Secret 
-  * Gespeichert AES-256-GCM-verschlüsselt in der Gateway-Datenbank (CWE-256) 
-  * Kein Plaintext auf Disk 
- 
-**Validierungsfenster:** ±1 Zeitfenster (30 s × 3 = 90 s Toleranz für Uhrabweichung) 
- 
-**Lockout-Policy:** Bestehender ''FailedAuthPerMinute''-Alert-Counter wird genutzt. 
-Nach 5 Fehlversuchen: Challenge ungültig, neuer ''POST /api/v1/auth/external'' nötig. 
- 
-**Challenge-Lifetime:** 5 Minuten. Challenges in Gateway-RAM (''TDictionary<String, TChallengeEntry>''), kein DB-Roundtrip. 
- 
-==== Enrollment Flow ==== 
- 
-<code> 
-Admin-seitig: 
-  GET /api/v1/auth/totp/enroll   (JWT des Admin-Users) 
-  → { "otpauth_uri": "otpauth://totp/...", "qr_base64": "..." } 
-  → Admin zeigt QR dem User (oder schickt URI per E-Mail) 
- 
-Self-Service (Shell öffnet WebView): 
-  Shell navigiert zu: https://gateway/auth/enroll?token=<JWT> 
-  → Gateway zeigt HTML-Seite mit QR-Code 
-  → User scannt mit MS Authenticator 
-  → Enrollment-Bestätigung (User gibt ersten Code ein zum Verifizieren) 
-</code> 
- 
-===== PFX Install-ID Validierung ===== 
- 
-Das Gateway prüft bei jedem Auth-Request ob die ''install_id'' aus dem 
-PFX registriert und aktiv ist. 
- 
-==== Header-Konvention ==== 
- 
-<code> 
-X-WvdS-Install-Id: ENIVERS-2026-001 
-</code> 
- 
-Dieser Header wird von der Shell bei **jedem** Request mitgesendet — nicht nur beim Auth-Request. 
- 
-==== Validierungslogik ==== 
- 
-<code> 
-  1. X-WvdS-Install-Id Header vorhanden? 
-  2. install_id in Deployment-Tabelle (DB) vorhanden und aktiv? 
-  3. Zugehöriges Ablaufdatum noch nicht überschritten? 
-  4. → Validierung OK: weiter mit Auth-Flow 
-  5. → Validierung FAIL: HTTP 403 "unlicensed installation" 
-</code> 
- 
-Eine neue Tabelle (z. B. ''WVDS_DEPLOYMENTS'') in der Gateway-Datenbank speichert: 
- 
-^ Spalte           ^ Typ          ^ Beschreibung                         ^ 
-| ''INSTALL_ID''   | VARCHAR(64)  | PK — z. B. ''ENIVERS-2026-001''      | 
-| ''TENANT_NAME''  | VARCHAR(256) | Anzeigename                          | 
-| ''LICENSE_TIER'' | VARCHAR(32)  | ''professional'' / ''enterprise''    | 
-| ''VALID_UNTIL''  | DATE         | Ablaufdatum (aus PFX synchronisiert) | 
-| ''DOMAINS''      | VARCHAR(512) | Erlaubte AD-Domänen (Komma-getrennt) | 
-| ''ACTIVE''       | BIT          | Manuell deaktivierbar                | 
- 
-===== Kerberos/SSPI Handler ===== 
- 
-==== Standalone-Modus ==== 
- 
-Wenn Gateway als Windows Service läuft (nicht hinter IIS/NGINX): 
-  * ''AcceptSecurityContext'' aus ''Secur32.dll'' (Windows SSPI API) 
-  * HTTP ''Authorization: Negotiate'' Header — mehrstufiger Handshake 
-  * Nach Erfolg: Identität via ''QueryContextAttributes(SECPKG_ATTR_NAMES)'' lesen 
-  * → JWT für diese Identität ausstellen 
- 
-==== FastCGI-Modus (hinter IIS) ==== 
- 
-IIS übernimmt Windows Auth vollständig: 
-  * IIS sendet ''LOGON_USER'' als CGI-Umgebungsvariable 
-  * Gateway-FastCGI-Adapter (''WvdS.Data.Gateway.FastCGI.Adapter'') liest ''LOGON_USER'' 
-  * Kein ''AcceptSecurityContext'' im Gateway nötig — IIS hat bereits validiert 
-  * Direkt JWT ausstellen 
- 
-> **Empfehlung:** FastCGI hinter IIS für Corpnet-Deployments. 
-> Standalone SSPI für Self-hosted Szenarien (kleinere Installationen). 
- 
-===== Middleware-Änderungen ===== 
- 
-==== Bestehende Pipeline (9 Stufen) ==== 
- 
-<code> 
-1. TWvdSMetricsMiddleware 
-2. TWvdSLoggingMiddleware 
-3. TWvdSHttpsEnforcementMiddleware 
-4. TWvdSRequestSizeMiddleware 
-5. TWvdSRateLimitMiddleware 
-6. TWvdSGatewayAuthHandler          ← Erweiterung: amBearer + amWindows 
-7. TWvdSPayloadEncryptionMiddleware 
-8. TWvdSCompressionMiddleware 
-9. MapControllers 
-</code> 
- 
-==== Geplante Erweiterungen ==== 
- 
-^ Stufe ^ Komponente                        ^ Änderung                                               ^ 
-| 3.5   | ''TWvdSInstallIdMiddleware'' (neu) | ''X-WvdS-Install-Id'' prüfen, vor Auth                 | 
-| 6     | ''TWvdSGatewayAuthHandler''        | ''amBearer'': JWT ML-DSA validieren + Claims extrahieren | 
-| 6     | ''TWvdSGatewayAuthHandler''        | ''amWindows'': SSPI Negotiate oder LOGON_USER (FastCGI) | 
- 
-''TWvdSInstallIdMiddleware'' greift vor der eigentlichen Auth — ein Request ohne 
-gültige Install-ID wird mit HTTP 403 abgelehnt, bevor Credentials geprüft werden. 
- 
-===== Neue Units ===== 
- 
-<code> 
-src/ 
-  controllers/ 
-    WvdS.Data.Gateway.Controller.Auth.pas       ← /api/v1/auth/*, /api/v1/features 
-  core/ 
-    WvdS.Data.Gateway.Auth.JwtService.pas       ← JWT Ausstellung (ML-DSA-65), Validierung 
-    WvdS.Data.Gateway.Auth.TotpService.pas      ← Shared Secret, Challenge, HMAC-SHA1 (OpenSSL) 
-    WvdS.Data.Gateway.Auth.KerberosHandler.pas  ← SSPI AcceptSecurityContext (Windows-only) 
-    WvdS.Data.Gateway.Auth.CertValidator.pas    ← Install-ID gegen DB prüfen 
-    WvdS.Data.Gateway.Auth.RefreshStore.pas     ← Refresh Token (gehashed, DB-backed) 
-  middleware/ 
-    WvdS.Data.Gateway.Middleware.InstallId.pas  ← X-WvdS-Install-Id Middleware 
-</code> 
- 
-Alle neuen Units im ''src/core/'' und ''src/middleware/'' — passend zur 
-bestehenden Schichtenstruktur. 
- 
-===== Security — CWE-Ergänzungen ===== 
- 
-Zusätzlich zu den bestehenden 22 CWEs kommen durch die Auth-Erweiterung: 
- 
-^ CWE     ^ Beschreibung                    ^ Implementierung                                                          ^ 
-| CWE-287 | Improper Authentication         | Kerberos-Ticket muss vollständig validiert sein (nicht nur Presence-Check) | 
-| CWE-290 | Token Spoofing                  | JWT-Signatur (ML-DSA-65) wird bei jedem Request geprüft                  | 
-| CWE-613 | Insufficient Session Expiration | Refresh Token: 30 Tage, serverseitig revokierbar                         | 
-| CWE-620 | Unverified Password Change      | TOTP-Enrollment erfordert bestätigten ersten Code                        | 
-| CWE-384 | Session Fixation                | ''jti'' (JWT ID) einmalig, Replay via ''jti''-Blacklist                  | 
- 
-===== Design-Entscheidungen (protokolliert) ===== 
- 
-^ Thema                  ^ Entscheidung                                                       ^ 
-| JWT-Signatur           | ML-DSA-65 — Bibliothek bereits vorhanden, kein neuer Crypto-Code   | 
-| TOTP-Crypto            | HMAC-SHA1 via OpenSSL — kein neuer Crypto-Code                     | 
-| Kerberos Standalone    | ''Secur32.dll'' SSPI direkt; FastCGI-Modus bevorzugt hinter IIS    | 
-| Install-ID Transport   | HTTP-Header ''X-WvdS-Install-Id'' bei jedem Request                | 
-| Feature-Flags          | Nicht im JWT — separater Endpoint, frisch laden                    | 
-| Refresh Token Storage  | Gehashed (SHA-256) in DB — nie im Klartext gespeichert             | 
-| TOTP Enrollment        | Admin-seitig (primär) oder Self-Service WebView (BYOD)             | 
  
Zuletzt geändert: den 05.03.2026 um 14:37