2. Auth-Architektur: WvdS.Shell (Benutzeroberfläche)

Ausgangssituation

Die bisherigen WvdS-Clients — AMED und LD — liefen als native Windows-Anwendungen mit impliziter Windows-Authentifizierung. Das funktionierte gut, solange der Rechner in der Domäne eingebunden war und der Benutzer sich ohnehin am Domain-Account anmeldete. Es funktionierte nicht, sobald jemand außerhalb des Firmennetzes arbeitete, ein privates Gerät nutzte oder das System auf einem Rechner gestartet wurde, der keine interaktive Domänenanmeldung kannte.

WvdS.Shell löst dieses Problem durch eine klare Schichttrennung. Die erste Schicht — das PFX-Zertifikat — prüft, ob die Installation selbst berechtigt ist: ob sie lizenziert ist, ob sie für diese Domäne und diesen Benutzer gilt, ob das Zertifikat noch gültig ist. Diese Prüfung geschieht vollständig lokal, ohne Netzwerkzugriff. Erst wenn sie bestanden ist, beginnt die zweite Schicht: die Benutzerauthentifizierung über den Gateway.Service.

Das hat einen konkreten Effekt: Extensions laden nicht, und keine Business-Daten sind sichtbar, bevor der Benutzer vollständig authentifiziert ist. Es gibt keine Race Condition zwischen „Fenster ist offen“ und „Auth ist fertig“. Gleichzeitig ist der Offline-Betrieb — wenn das Gateway nicht erreichbar ist — kein Sonderfall, der nachträglich angeflickt wird, sondern ein eingeplantes Szenario mit eigenem Auth-Pfad und definiertem Umfang.

Konzept

WvdS.Shell verwendet eine zweischichtige Auth-Architektur:

  1. Schicht 1 — PFX-Zertifikat (Installations-/Lizenznachweis): Wird von WvdS ausgestellt und trägt Lizenz- und Deployment-Metadaten als Custom-Extensions. Wird lokal validiert — auch offline.
  2. Schicht 2 — Identity Provider (Benutzer-Authentifizierung): Kerberos (SSO, on-net) oder externer MFA-Flow (off-net). Liefert einen JWT Session Token.

Beide Schichten müssen erfolgreich sein, bevor eine Extension lädt oder Business-Daten sichtbar sind.

Schicht 1 — PFX-Zertifikat

Zweck

Das PFX (PKCS#12) dient gleichzeitig als:

  • Lizenznachweis — Was ist lizenziert? Welche Features? Bis wann?
  • Deployment-Nachweis — Für welche Domänen/User ist die Installation freigegeben?
  • Offline-Credential — Wenn Gateway nicht erreichbar, gilt ein valides PFX als Zugangsnachweis für den Offline-Modus.

Ausstellung

WvdS betreibt eine interne CA. Jede Kundeninstallation erhält ein signiertes PFX:

  • Signiert mit WvdS-CA (Root-Cert wird im Shell-Binary oder bekanntem Store-Pfad mitgeliefert)
  • Laufzeit typisch 1 Jahr (Renewal-Flow ist Aufgabe des Gateway-Service)
  • Kein Wildcard — pro Deployment ein eigenes Zertifikat

Custom Extensions (OID-Struktur)

Die Custom-Attributes werden als X.509v3-Extensions mit privatem OID-Arc geschrieben. Vorgeschlagener Arc: 1.3.6.1.4.1.XXXXX.1.* (IANA Private Enterprise Number von WvdS).

OID-Suffix Attribut-Name Beispielwert Beschreibung
.1 wvds.license.tier professional Lizenz-Stufe
.2 wvds.license.expires 2027-01-01 Ablaufdatum (ISO 8601)
.3 wvds.auth.domains ENI,VERSALIS Erlaubte AD-Domänen (Komma-getrennt)
.4 wvds.auth.users * Erlaubte Usernamen oder * für alle
.5 wvds.install.id ENIVERS-2026-001 Eindeutige Installations-ID
.6 wvds.tenant.name ENI VERSALIS GmbH Anzeigename der Installation
Nicht im PFX: wvds.license.features — Feature-Flags werden nach Auth aus der DB geladen (siehe Feature-Flags / ENIVERSASYS).

Deployment-Varianten

Variante Speicherort Schutzmechanismus Warum genau so?
Corpnet Windows Cert Store (LocalMachine\My) Windows ACL, GPO-Deployment Kein Benutzereingriff nötig — die IT verteilt das Zertifikat per GPO. Der Private Key ist durch Windows ACL vor anderen Nutzern geschützt.
BYOD / Extern Datei %APPDATA%\WvdS\license.pfx Passwort in DPAPI SecretStorage Kein Cert Store auf Privatgeräten. DPAPI bindet das PFX-Passwort ans Windows-Benutzerkonto — ein anderer Benutzer desselben PCs kann die Datei nicht entschlüsseln.

Cert-Store-Variante: Kein Passwort nötig — Windows schützt den Private Key. Shell sucht Zertifikat anhand Issuer-DN automatisch.

Validierung

Shell-Start
      │
      ▼
┌─ PFX Validator ──────────────────────────────────────────────┐
│  1. PFX im Cert Store oder Datei vorhanden?                  │
│  2. Signaturkette gültig? (gegen gebündelten WvdS-Root)      │
│  3. Zertifikat nicht abgelaufen?                             │
│  4. wvds.auth.domains enthält aktuelle Domäne oder '*'?      │
│  5. wvds.auth.users enthält aktuellen User oder '*'?         │
│  6. Custom-Attributes laden → License-Objekt befüllen        │
└──────────────────────────────────────────────────────────────┘
         │ OK                        │ FAIL
         ▼                           ▼
  Context-Detector           Shell zeigt nativen Fehler-Dialog:
  (Schicht 2)                "Kein gültiges Lizenzzertifikat"
                             → Shell-Start abgebrochen

Schicht 2 — Identity Provider

Context-Aware Auth (Netzwerk-Erkennung)

      │
      ▼
┌─ Context-Detector ────────────────────────────────────┐
│  1. Domain-joined?     → SSPI/Kerberos verfügbar?    │
│  2. DC erreichbar?     → LDAP-Ping                   │
│  3. Gateway erreichbar? → HTTP Health-Check          │
└───────────────────────────────────────────────────────┘
         │
   ┌─────┴──────────┬────────────────┐
   ▼                ▼                ▼
 Corpnet          Extern          Offline
(Kerberos)      (MFA-Flow)      (PFX-Only)

Szenario A — Corpnet (LAN/WLAN)

  • Domain-joined + DC erreichbar + Gateway erreichbar
  • SSPI/Kerberos → Service Ticket für Gateway.Service
  • Kein Dialog, kein Klick — SSO out of the box
  • PFX-Domänen-Attribut wird gegen aktuelle AD-Domäne geprüft

Szenario B — Home-Office mit VPN

  • VPN-Tunnel stellt DC-Erreichbarkeit wieder her → identisch wie Szenario A
  • Shell führt bei Gateway-Unerreichbarkeit automatische Retries durch (konfigurierbarer Timeout), bevor auf Szenario C/D eskaliert wird

Szenario C — Extern / BYOD (MFA-Flow)

  • DC nicht erreichbar, kein VPN, Gateway aber erreichbar
  • Nativer Auth-Dialog (pre-WebView, FPC/Lazarus nativ)
  • Ablauf:
    1. Username + Passwort → Gateway
    2. Gateway sendet TOTP-Challenge
    3. User öffnet MS Authenticator (OATH TOTP) → 6-stelliger Code
    4. Gateway validiert → Access Token (JWT, kurzlebig) + Refresh Token (langlebig)
    5. Refresh Token → SecretStorage (DPAPI)

Szenario D — Offline (kein Gateway erreichbar)

  • Gateway nicht erreichbar (Server down, kein Netz)
  • Gültiges PFX vorhanden + nicht abgelaufen → Offline-Modus
  • Context Key auth.mode = offline wird gesetzt
  • Extensions können per when-Clause offline-sensitive Features deaktivieren
  • Kein Refresh Token nötig — PFX ist der Credential
  • Offline-Modus-Dauer: bis PFX-Ablauf oder Gateway wieder erreichbar (Background-Retry)

Feature-Flags / ENIVERSASYS

Feature-Flags steuern, welche Funktionen in der Shell und in Extensions aktiv sind. Sie sind kein Bestandteil des PFX — das PFX kennt nur die Lizenz-Stufe (tier). Die granularen Feature-Flags werden nach erfolgreicher Auth aus der DB geladen.

Herkunft und Migration

Stand Namespace System
Aktuell auth.* ENIVERSCAFM
Zukünftig auth.* ENIVERSASYS

Die Shell spricht ausschließlich gegen das Gateway.Service. Das Gateway abstrahiert, ob es ENIVERSCAFM oder ENIVERSASYS als Quelle verwendet — die Shell bekommt immer dasselbe Antwortformat (JSON-Array der aktiven Feature-Keys).

Ladereihenfolge

Feature-Flags werden innerhalb von spBlockAuth geladen, nach dem JWT-Erhalt und vor AdvancePhase(spBlockRestore). Extensions dürfen in when-Clauses auf Feature-Flags zugreifen, also müssen sie vor dem Extension-Scan verfügbar sein.

   Auth erfolgreich (JWT vorhanden)
         │
         ▼
   GET /api/features  →  Gateway.Service  →  ENIVERSASYS-DB
         │
         ▼
   Context Keys befüllen:
     license.features = grids,charts,scheduling,...
         │
         ▼
   AdvancePhase(spBlockRestore)

Offline-Verhalten

Im Offline-Modus (Szenario D) ist kein Gateway-Aufruf möglich. Zwei Optionen — Entscheidung offen:

  • Letzte bekannte Features aus SecretStorage cachen (nach jedem erfolgreichen Online-Login schreiben)
  • Nur Basisfunktionen im Offline-Modus — Features deaktiviert bis Gateway erreichbar

TOTP — Aufgabenteilung

TOTP (RFC 6238) basiert auf einem geteilten Geheimnis zwischen Authenticator-App und Server. Die Kernfrage: Was muss WvdS.Shell selbst implementieren?

Algorithmus (zur Orientierung)

Gemeinsames Geheimnis (base32) → bei Enrollment einmalig zwischen App und Server ausgetauscht
Counter = floor(unix_timestamp / 30)   ← 30-Sekunden-Fenster
Code = HMAC-SHA1(secret, counter) → auf 6 Dezimalstellen gekürzt

MS Authenticator, Google Authenticator und jede andere OATH-TOTP-App erzeugen denselben Code, solange sie dasselbe Geheimnis und eine synchrone Uhrzeit haben.

Was Gateway.Service implementiert

Das ist ausschließlich Server-Aufgabe:

  • Shared Secret pro User erzeugen und sicher speichern
  • Enrollment-URI (otpauth-Schema) und QR-Code für Authenticator-App generieren
  • Eingehenden 6-stelligen Code validieren (HMAC-SHA1, ±1 Fenster Toleranz für Uhrzeitabweichung)
  • Enrollment-Status pro User verwalten
  • Fehlversuche zählen, Lockout-Policy durchsetzen

Was WvdS.Shell implementiert

Nahezu nichts vom Algorithmus. Die Shell ist nur ein UI-Träger für den Challenge-Response-Austausch:

  1. Challenge erkennen: Gateway antwortet mit HTTP 401 + JSON-Body {„mfa_required“: true, „challenge_id“: „…“}
  2. Nativen TOTP-Eingabe-Dialog anzeigen (6 Ziffern, pre-WebView, FPC/Lazarus)
  3. Optionaler 30-Sekunden-Countdown (UX-Hilfe, kein funktionaler Bedarf)
  4. Code + challenge_id an Gateway senden
  5. Falsche Eingabe: Fehlermeldung anzeigen, Retry erlauben (Gateway definiert Max-Versuche)
  6. Kein Zugriff auf das Shared Secret — liegt ausschließlich beim Gateway

Enrollment-Flow

Enrollment (erstmalige Einrichtung) ist kein Shell-Startup-Vorgang. Zwei Optionen:

Option Umsetzung Wann sinnvoll
Admin-seitig Gateway-Weboberfläche, Admin verknüpft User mit Authenticator-App Verwaltete Deployments
Self-Service im Shell Shell öffnet Gateway-Enrollment-URL in dediziertem WebView-Tab BYOD, erster Start ohne Enrollment

Beim Self-Service-Flow zeigt die Shell einen WebView, der auf die Gateway-Enrollment-Seite zeigt. Die Shell selbst generiert dabei nichts — der gesamte Enrollment-Prozess läuft im Gateway.

Zusammenfassung: Was WvdS.Shell selbst codiert

  • WvdS.Shell.Auth.Provider.TOTP.pas — nur Challenge/Response-Protokoll (HTTP-Austausch)
  • WvdS.Shell.GUI.AuthDialog.pas — 6-Ziffern-Eingabefeld + optionaler Countdown-Timer
  • Kein HMAC, kein base32, kein Geheimnis-Management — das gehört zum Gateway

Gateway-seitige TOTP-Implementierung (Shared Secret, HMAC-SHA1, Lockout): 3. Gateway-Doku: TOTP Service

Token-Flow zwischen Shell und Gateway.Service

Shell (Client)              Gateway.Service
      │                            │
      │── SSPI Negotiate ─────────►│  (Kerberos — Szenario A/B)
      │   + PFX install.id         │  → DC validiert Kerberos
      │◄─ Session JWT ─────────────│  → Gateway prüft PFX install.id
      │                            │
      │  oder:                     │
      │── username/pw + install.id►│  (Szenario C)
      │◄─ TOTP-Challenge ──────────│
      │── TOTP-Code ──────────────►│
      │◄─ {access_token, refresh} ─│
      │                            │
      │── refresh_token ──────────►│  (Silent Refresh, Hintergrund)
      │◄─ neues access_token ──────│

Extensions bekommen nie den Refresh Token. Via IPC (JSON-RPC) fordern Extensions ein scoped Token an — der Shell-AuthManager tauscht das Shell-Token gegen ein Extension-spezifisches Token beim Gateway.

Technische Details der Gateway-Endpoints (Kerberos, TOTP, JWT, Refresh): 3. Gateway-Doku: Neue Endpoints

Startup-Phasen-Integration

In WvdS.Shell.Types.pas wird spBlockAuth eingefügt:

TWvdSShellStartupPhase = (
  spBlockStartup,   // Config, Log init
  spBlockAuth,      // [WvdS 2026-03-05] PFX-Validation + Identity Provider
  spBlockRestore,   // Extension scan, contribution routing
  spAfterRestored,  // Activate startup extensions, show UI
  spEventually      // Deferred tasks (auto-update check)
);

Reihenfolge innerhalb von spBlockAuth:

  1. PFX laden und validieren (Schicht 1)
  2. PFX-Attribute in Context Keys schreiben (tier, expires, domains, install.id)
  3. Context-Detector ausführen (Schicht 2)
  4. Auth-Provider starten (Kerberos / MFA / Offline)
  5. Auth-Context Keys setzen (state, method, username, mfa)
  6. Feature-Flags aus ENIVERSASYS via Gateway laden → license.features setzen
  7. AdvancePhase(spBlockRestore) — erst jetzt startet Extension-Scan

Context Keys nach Auth

Key Werte / Beispiel Beschreibung
auth.state authenticated / pending / failed Auth-Status
auth.mode online / offline Gateway erreichbar?
auth.method windows / external Verwendetes Verfahren
auth.username UPN oder Benutzername Aktuell angemeldeter User
auth.mfa true / false MFA wurde durchgeführt
license.tier professional / enterprise Aus PFX (.1)
license.expires 2027-01-01 Aus PFX (.2)
license.installId ENIVERS-2026-001 Aus PFX (.5)
license.features grids,charts,scheduling Aus ENIVERSASYS-DB (nach Auth)

Beispiel-when-Clause in einem Extension-Manifest:

"when": "auth.state == authenticated && license.features contains charts"

Vorgeschlagene Unit-Struktur

Security/
  WvdS.Shell.Security.SecretStorage.pas            ← bereits vorhanden (DPAPI)
  Auth/
    WvdS.Shell.Auth.Types.pas                      ← Enums, Records, IAuthProvider
    WvdS.Shell.Auth.Certificate.pas                ← PFX laden, Kette validieren
    WvdS.Shell.Auth.License.pas                    ← Custom-Attributes lesen, Feature-Flags
    WvdS.Shell.Auth.ContextDetector.pas            ← Domain-Join + DC-Ping + Gateway-Check
    WvdS.Shell.Auth.SessionManager.pas             ← Token-Lifecycle, Silent Refresh, Offline-Retry
    Providers/
      WvdS.Shell.Auth.Provider.Kerberos.pas        ← SSPI/Kerberos (Windows-only)
      WvdS.Shell.Auth.Provider.External.pas        ← JWT-basiert (HTTP + Gateway)
      WvdS.Shell.Auth.Provider.TOTP.pas            ← Challenge-Response Handling
      WvdS.Shell.Auth.Provider.Offline.pas         ← PFX-basierter Offline-Pfad

GUI/
  WvdS.Shell.GUI.AuthDialog.pas                    ← Nativer Login-Dialog (pre-WebView)
  WvdS.Shell.GUI.OfflineBanner.pas                 ← StatusBar-Hinweis im Offline-Modus

WvdS.Shell.Auth.Types importiert nur RTL — null Shell-interne Deps, passend zur Schichten-Regel.

Design-Entscheidungen (protokolliert)

Thema Entscheidung Begründung
Multi-Tenant Nicht in Phase 1. Single-Tenant-Deployment. Erweiterbar via wvds.tenant.*-Attributes. Reduziert Komplexität für den Erstaufbau. Die OID-Struktur ist vorbereitet — Multi-Tenant ist eine Konfigurationserweiterung, keine Architekturänderung.
SSPI-Fallback Kein NTLM-Fallback. 2026 wird Kerberos vorausgesetzt. NTLM ist anfällig für Pass-the-Hash-Angriffe und gilt als veraltet. Kein Fallback zu verwalten bedeutet eine kleinere Angriffsfläche.
Offline-Modus PFX als Offline-Credential. Dauer bis PFX-Ablauf oder Gateway-Reconnect. Ein Serverausfall darf den Betrieb nicht blockieren. Das PFX ist kryptografisch signiert — es kann ohne Serververbindung sicher geprüft werden.
PFX-Speicher Cert Store (Corpnet/GPO) oder Datei + DPAPI-Passwort (BYOD). Beide Varianten nutzen plattformeigene Schutzmechanismen. Kein eigenes Passwort-Management, keine eigene Verschlüsselung — Windows übernimmt das.
SSO Out-of-the-box via Kerberos. Kein Benutzereingriff bei Corpnet. Ein Passwort-Dialog beim Unternehmensrechner ist vermeidbare Reibung. Kerberos nutzt das bereits vorhandene Domain-Login — null Zusatzaufwand für den Benutzer.
CA WvdS-interne CA. Root-Cert im Shell-Binary gebündelt. Kein Abhängigkeit von öffentlichen CAs oder externen Zertifikatsdiensten. WvdS kontrolliert Ausstellung, Laufzeit und Widerruf vollständig selbst.
Zuletzt geändert: den 05.03.2026 um 23:34