3. Gateway.Service — Auth-Erweiterungen für WvdS.Shell

Stand: 2026-03-05

Übergeordnet: Auth-Architektur — Gesamtübersicht Verwandt: 2. WvdS.Shell | 4. Datenschicht | 5. Migration

Ausgangssituation

Das Gateway.Service existierte bereits als HTTP-REST-Gateway mit einer vollständigen Middleware-Pipeline — Logging, Rate-Limiting, HTTPS-Enforcement, Routing. Was fehlte, war ein vollständiges Auth-Subsystem für interaktive Benutzer. Die vorhandenen Modi (Basic, ApiKey) waren für Dienst-zu-Dienst-Kommunikation ausgelegt, nicht für Session-Management mit Token-Lifecycle und Mehrfaktor-Authentifizierung.

Ein Gateway ohne vollständiges Auth-Subsystem bedeutet: Entweder werden Credentials bei jedem Request übertragen — ein fundamentales Sicherheitsproblem — oder der Client muss selbst den Zustand verwalten, was die Architektur unnötig verkompliziert. JWT-Tokens lösen das: Ein einmaliger Auth-Vorgang erzeugt ein kurzlebiges Access Token und ein langlebiges Refresh Token. Alle nachfolgenden Requests sind stateless und tragen nur den signierten Token — kein Passwort, kein serverseitiger Session-State.

Die Wahl von ML-DSA-65 für die JWT-Signierung ist dabei keine akademische Entscheidung. Klassische Signaturverfahren (RSA, ECDSA) könnten durch Quantencomputer kompromittiert werden. Mit ML-DSA-65 als post-quanten-sicherem Verfahren bleibt die Plattform auch dann vertrauenswürdig, wenn klassische Kryptografie in absehbarer Zeit nicht mehr ausreicht — ohne dass bestehende Token-Strukturen nachträglich geändert werden müssten.

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:

  1. PFX Install-ID Validierung (amCertificate)
  2. Kerberos/SSPI Handler (amWindows)
  3. JWT Ausstellung und Validierung (amBearer)
  4. TOTP Service (neu — für externen MFA-Flow)
  5. Refresh Token Management (neu)
  6. 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 Warum so gestaltet?
POST /api/v1/auth/kerberos Nein (ist der Auth-Schritt selbst) Kerberos Negotiate-Handshake Der Handshake selbst ist der Auth-Vorgang — ein Token als Voraussetzung wäre ein Henne-Ei-Problem.
POST /api/v1/auth/external Nein Username/Passwort → TOTP-Challenge Kein direktes Token bei Passwort allein: Erst nach TOTP-Bestätigung gilt die Identität als gesichert.
POST /api/v1/auth/totp/verify Nein (Challenge-ID statt Token) TOTP-Code einlösen → JWT Die Challenge-ID ist temporär und wertlos ohne den passenden Code — kein Missbrauch ohne gültigen TOTP.
POST /api/v1/auth/token/refresh Refresh Token Access Token erneuern Silent Refresh im Hintergrund — der Benutzer erlebt keinen Logout, solange er aktiv ist.
GET /api/v1/auth/totp/enroll JWT Bearer TOTP Enrollment-URI + QR generieren Enrollment erfordert eine laufende Session — verhindert unbefugtes Einrichten fremder TOTP-Secrets.
DELETE /api/v1/auth/totp/enroll JWT Bearer TOTP-Enrollment zurücksetzen Nur der Kontoinhaber selbst (oder ein Admin) kann das TOTP-Geheimnis löschen.
GET /api/v1/features JWT Bearer Feature-Flags für aktuelle Session Feature-Flags außerhalb des JWT: Änderungen greifen sofort ohne Token-Neuausstellung.

POST /api/v1/auth/kerberos

Kerberos Negotiate-Handshake (mehrstufig, wie HTTP Negotiate definiert):

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 }

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.

Request:
  { "username": "max.muster", "password": "...", "install_id": "ENIVERS-2026-001" }
 
Response 200:
  { "mfa_required": true, "challenge_id": "c7f3a..." }
 
Response 401:
  { "error": "invalid_credentials" }

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.

Request:
  { "challenge_id": "c7f3a...", "code": "482910" }
 
Response 200:
  { "access_token": "...", "refresh_token": "...", "expires_in": 900 }
 
Response 401:
  { "error": "invalid_code", "attempts_remaining": 2 }

POST /api/v1/auth/token/refresh

Silent Token Refresh. Refresh-Token wird im Request-Body oder als Authorization: Refresh <token> übergeben.

Request:
  { "refresh_token": "..." }
 
Response 200:
  { "access_token": "...", "expires_in": 900 }
 
Response 401:
  { "error": "refresh_token_expired" }

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.

Response 200:
  { "features": ["grids", "charts", "scheduling", "docking"] }

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: jwt

Payload-Struktur

{
  "sub":        "max.muster@corp.example.com",
  "install_id": "ENIVERS-2026-001",
  "auth_method": "windows",
  "mfa":        false,
  "iat":        1741132800,
  "exp":        1741133700,
  "jti":        "a3f9..."
}

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) Begründung
Access Token 15 Minuten RAM (Shell SessionManager) Ein gestohlenes Token ist nach maximal 15 Minuten wertlos — ohne Admin-Eingriff. RAM statt Datei: Kein Risiko durch Dateisystem-Analyse; Neustart löscht das Token automatisch.
Refresh Token 30 Tage DPAPI SecretStorage Bequeme Wiederanmeldung ohne täglichen Login. DPAPI bindet das Speichergeheimnis an das Windows-Benutzerkonto — andere Benutzer desselben PCs kommen nicht an das Token.
TOTP Challenge 5 Minuten Gateway-RAM (nach Ablauf gelöscht) Ein abgefangener 6-stelliger Code ist nach 5 Minuten wertlos. Kein Disk-Zustand: Ein Serverneustart bereinigt alle offenen Challenges automatisch.

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: 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

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)

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

X-WvdS-Install-Id: ENIVERS-2026-001

Dieser Header wird von der Shell bei jedem Request mitgesendet — nicht nur beim Auth-Request.

Validierungslogik

  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"

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)

1. TWvdSMetricsMiddleware
2. TWvdSLoggingMiddleware
3. TWvdSHttpsEnforcementMiddleware
4. TWvdSRequestSizeMiddleware
5. TWvdSRateLimitMiddleware
6. TWvdSGatewayAuthHandler          ← Erweiterung: amBearer + amWindows
7. TWvdSPayloadEncryptionMiddleware
8. TWvdSCompressionMiddleware
9. MapControllers

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

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

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 Begründung
JWT-Signatur ML-DSA-65 — Bibliothek bereits vorhanden, kein neuer Crypto-Code Post-quanten-sicher: Klassische Signaturverfahren (RSA, ECDSA) können durch Quantencomputer gebrochen werden. ML-DSA-65 ist heute zukunftssicher — ohne spätere Architekturänderung.
TOTP-Crypto HMAC-SHA1 via OpenSSL — kein neuer Crypto-Code RFC 6238 definiert HMAC-SHA1 als TOTP-Standard. Vorhandene OpenSSL-Integration nutzen statt neuen Crypto-Code einzuführen — weniger Fehlerquellen.
Kerberos Standalone Secur32.dll SSPI direkt; FastCGI-Modus bevorzugt hinter IIS FastCGI hinter IIS: IIS übernimmt Windows Auth vollständig, das Gateway braucht keinen SSPI-Code. Standalone SSPI bleibt als Fallback für selbst-gehostete Umgebungen ohne IIS.
Install-ID Transport HTTP-Header X-WvdS-Install-Id bei jedem Request Jeder API-Aufruf trägt die Installations-ID — nicht nur der Login. Das Gateway kann eine deaktivierte Lizenz sofort bei jedem Request abweisen, nicht erst beim nächsten Login.
Feature-Flags Nicht im JWT — separater Endpoint, frisch laden Flags im JWT würden eingefroren bleiben bis zum Token-Ablauf. Frisch geladen: Lizenzänderungen oder Feature-Aktivierungen greifen ohne Token-Neuausstellung sofort.
Refresh Token Storage Gehashed (SHA-256) in DB — nie im Klartext gespeichert Ein Datenbankdump gibt keinen verwertbaren Refresh Token preis. Nur der Client kennt das Klartext-Token — das Gateway kann nur prüfen, nicht rekonstruieren.
TOTP Enrollment Admin-seitig (primär) oder Self-Service WebView (BYOD) Admin-seitig: IT kontrolliert den Prozess bei verwalteten Geräten. Self-Service: BYOD-Nutzer können eigenständig einrichten ohne IT-Eingriff — beide Wege über denselben Gateway-Enrollment-Endpoint.
Zuletzt geändert: den 05.03.2026 um 23:36