Dies ist eine alte Version des Dokuments!


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

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
Corpnet Windows Cert Store (LocalMachine\My) Windows ACL, GPO-Deployment
BYOD / Extern Datei %APPDATA%\WvdS\license.pfx Passwort in DPAPI SecretStorage

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
Multi-Tenant Nicht in Phase 1. Single-Tenant-Deployment. Erweiterbar via wvds.tenant.*-Attributes.
SSPI-Fallback Kein NTLM-Fallback. 2026 wird Kerberos vorausgesetzt.
Offline-Modus PFX als Offline-Credential. Dauer bis PFX-Ablauf oder Gateway-Reconnect.
PFX-Speicher Cert Store (Corpnet/GPO) oder Datei + DPAPI-Passwort (BYOD).
SSO Out-of-the-box via Kerberos. Kein Benutzereingriff bei Corpnet.
CA WvdS-interne CA. Root-Cert im Shell-Binary gebündelt.
Zuletzt geändert: den 05.03.2026 um 14:21