Zum Inhalt

Operator-UI Keycloak-Client

Public-SPA-Client im kora-platform-Realm für die Operator-Administrations- Oberfläche (Tenants, Templates, Module, Audit-Log-View).

Deklariert in infra/keycloak/realms/kora-platform-realm.json unter clients[clientId=operator-ui]. Mitgeliefert seit Block 7.1a (2026-04-24).

Design-Entscheidungen

  • Public-Client, PKCE S256 required — analog zum tenant-ui-Client (Block 5/7a) im kora-tenants-Realm. SPA läuft im Browser, kein Secret.
  • Path-basiertes Routing statt Subdomain. Die Operator-UI wird unter https://platform.kora.luki-net.org/admin/operator/* ausgeliefert; es gibt keine separate Subdomain admin.kora.luki-net.org. Der Grund: die Platform-Subdomain existiert bereits (NPMplus-Proxy + TLS-Zertifikat), der zusätzliche Hop für eine Operator-Subdomain hätte nur den Nutzen einer symbolischen Trennung und keine weitere Sicherheits- oder Caching-Eigenschaft. Falls sich das später ändert (z. B. strengere Split-CORS-Policies): Subdomain nachziehen, Client-redirectUris und webOrigins entsprechend erweitern.
  • Realm-Default-Session-Lifetimes. Anders als die Vendor-Breakglass- und -Tunnel-Clients (2h Client-Session-Max) bekommt operator-ui keine harten Session-Caps — Operator-Sessions sind tagesgängig und sollen nicht alle zwei Stunden zum Re-Login zwingen. Session-Hygiene läuft über Keycloak-Realm-Defaults + Audit-Log.
  • Dev-Port 5174. tenant-ui belegt Vite-Standard 5173; operator-ui bekommt den nächsten freien Vite-Port, damit 7.1b beide SPAs parallel lokal laufen lassen kann.

Redirect-URIs (vollständig)

Umgebung URI
Prod — Callback https://platform.kora.luki-net.org/admin/operator/auth/callback
Prod — Wildcard https://platform.kora.luki-net.org/admin/operator/*
Dev (Vite) — Callback http://localhost:5174/auth/callback
Dev (Vite) — Wildcard http://localhost:5174/*
Dev (API-only) — Callback http://localhost:8280/admin/operator/auth/callback
Dev (API-only) — Wildcard http://localhost:8280/admin/operator/*

webOrigins spiegelt dieselben Host-Bereiche. post.logout.redirect.uris nutzt dieselbe Liste plus den allgemeinen Platform-Root.

Dev-Redirect-Patch

infra/keycloak/patch-dev-redirects.sh ist um operator-ui erweitert. Der Fallback-Dev-Redirect (http://localhost:8280/*, DEV_ORIGIN) wird für alle vier Platform-Realm-Clients ergänzt, damit Host-basierte Redirects im lokalen Stack greifen, auch wenn kcadm.sh create das nicht mit in den Re-Import gezogen hat.

export KC_BOOTSTRAP_ADMIN_PASSWORD=./infra/keycloak/patch-dev-redirects.sh

Wirkung: operator-ui bekommt zusätzlich den http://localhost:8280/*- Redirect, ohne die Realm-JSON-Quelle zu verändern. Prod-Deployments führen das Skript nicht aus.

Re-Import in laufende Keycloak-Instanz

Ein kc.sh import ... auf die Realm-JSON überschreibt vorhandene Client-Definitionen und löscht bereits erzeugte Client-Secrets (bei confidential-Clients) — für operator-ui ist das irrelevant, weil er public ist, aber der Import-Befehl als Ganzes kann die anderen Platform-Clients beeinflussen.

Empfohlener Pfad seit 2026-04-25: das Init-Script infra/keycloak/init-scripts/create-operator-ui-client.sh ausführen — extrahiert den Client-Block zur Laufzeit aus dem Realm-JSON und ruft kcadm.sh create clients -f. Idempotent: zweiter Aufruf erkennt den existierenden Client, skippt clean (kein Drift-Reset).

KC_BOOTSTRAP_ADMIN_PASSWORD= \
  ./infra/keycloak/init-scripts/create-operator-ui-client.sh

Für Neuinstallationen ist die nominelle Reihenfolge:

  1. Realm-JSON beim ersten Container-Start importieren (das passiert automatisch via kc.sh import im Keycloak-Bootstrap)
  2. Init-Scripts in Reihenfolge create-audit-service-account.sh, create-operator-ui-client.sh ausführen — beide idempotent

Ein erneuter kc.sh import auf die Realm-JSON ist nach dem Erstanlegen tabu, weil er bereits erzeugte Confidential-Client-Secrets (kora-api, kora-api-vendor-breakglass, kora-api-vendor-tunnel, kora-platform-audit) regeneriert und damit den deployten Stack zerlegt.

Drift-Vermeidung

Source-of-Truth-Modell: das Realm-JSON deklariert den Soll-Zustand; Init-Scripts in infra/keycloak/init-scripts/ garantieren den Ist-Zustand idempotent.

Hintergrund: ein Realm-JSON-Re-Import würde Confidential-Client-Secrets und das Audit-Service-Account-Secret regenerieren — deshalb ist der Re-Import nach dem Erstanlegen tabu, und neue/nachträgliche Clients werden über kcadm.sh create clients -f <client.json> nachgezogen. Das 2026-04-25 entdeckte operator-ui-Drift (Client im JSON deklariert, aber im laufenden Realm fehlend) ist mit create-operator-ui-client.sh geschlossen (archiviertes TODO-Platform-05).

Aufruf-Reihenfolge bei Realm-Reset / Fresh-Setup:

# 1. Beim Container-Erstanlauf zieht Keycloak die Realm-JSON automatisch.

# 2. Init-Scripts in Reihenfolge ausführen:
KC_BOOTSTRAP_ADMIN_PASSWORD=  # aus .env.platform
./infra/keycloak/init-scripts/create-audit-service-account.sh
./infra/keycloak/init-scripts/create-operator-ui-client.sh

# 3. Verifikations-Befehl als verbindlicher Abgleich gegen Erwartung:
docker exec kora-platform-keycloak \
  /opt/keycloak/bin/kcadm.sh get clients -r kora-platform \
    --fields clientId | sort

Erwartete Client-Liste (nach Init): account, account-console, admin-cli, broker, kora-api, kora-api-vendor-breakglass, kora-api-vendor-tunnel, kora-platform-audit, operator-ui, realm-management, security-admin-console. Abweichungen sind ein Hinweis auf einen weiteren Drift — neuen TODO eröffnen.

Caveat (Realm-JSON-Constraints): der Keycloak-DB-Schema-Constraint CLIENT.DESCRIPTION varchar(255) greift bei kcadm.sh create -f <client.json>. Längere Description-Strings würden zwar das Realm-JSON optisch akzeptieren, aber der Re-Create-Pfad scheitert mit value too long for type character varying(255). Beim Bearbeiten der Realm-JSON-Client-Blöcke deshalb auf Description-Länge achten — das gilt für jeden künftigen neuen Public- oder Confidential-Client.

Block-7.1b-Vorbereitung

Wenn das neue Vue-Projekt frontend/operator-ui/ in 7.1b aufgesetzt wird, muss dessen .env.development (oder Pendant) folgende Werte tragen:

VITE_KEYCLOAK_BASE_URL=http://localhost:8236
VITE_KEYCLOAK_REALM=kora-platform
VITE_KEYCLOAK_CLIENT_ID=operator-ui
VITE_OAUTH_REDIRECT_URI=http://localhost:5174/auth/callback
VITE_OAUTH_POST_LOGOUT_URI=http://localhost:5174/
VITE_API_BASE_URL=http://localhost:8280/api/v1

Der useAuth.ts-Composable aus frontend/tenant-ui/ ist bereits env-parametrisiert (Block 5 Sub-Commit 1) und lässt sich per Copy-Paste übernehmen — nur die Router-Base (/admin/operator/ statt /tenant/) und die Seiten selbst werden neu geschrieben.

Prod-Deployment: NPMplus-Routing (Block 7.1b Stand, Rollout Block 14)

Das Operator-UI-Asset-Bundle landet seit Block 7.1b im Platform-Image: der operator-ui-builder-Stage in infra/docker/Dockerfile.platform produziert dist/ und kopiert es nach /app/static/operator. Der FastAPI-Mount in src/kora_platform/main.py liefert die Assets bereits intern unter /admin/operator/* inklusive SPA-Fallback auf index.html für Vue-Router-Deep-Links.

NPMplus-Regel für platform.kora.luki-net.org (Skizze, nicht rolled out):

Location Target Hinweis
/admin/operator/ (Prefix) kora-platform-api:8080/admin/operator/ Das FastAPI-Backend liefert index.html + Assets direkt — keine separate NPMplus-Static-Location nötig.
/admin/operator/api/* nicht benötigt Ist ein Tippfehler-Vermeider: die API liegt unter /api/v1/*, nicht unter /admin/operator/api/*.
/api/v1/* kora-platform-api:8080/api/v1/ Bleibt wie seit 7.1a.

Der SPA-Fallback ist im FastAPI-Handler umgesetzt (@app.get("/admin/operator/{_path:path}")), nicht in NPMplus. Damit bleibt die Nginx-Config schlank: eine generische Proxy-Rule für die Platform-Subdomain reicht.

Block 14 (Deployment) rollt die produktive NPMplus-Regel aus und aktiviert sie; bis dahin ist der Prod-Zugriff nur nach make redeploy-platform auf der Platform-VM verfügbar und läuft direkt auf platform.kora.luki-net.org:8280.

Dev-Workflow

Für lokale Iteration läuft der Vite-Dev-Server parallel zum Backend:

# Terminal 1 — Backend
make dev-platform

# Terminal 2 — Operator-UI Vite-Dev (Port 5174)
make operator-ui-dev

Vite proxyt /api/* automatisch auf http://localhost:8280 (siehe frontend/operator-ui/vite.config.ts). Login gegen den lokalen Keycloak auf localhost:8236, Realm kora-platform, Client operator-ui.

E2E-Tests gegen Vite:

export KC_BOOTSTRAP_ADMIN_PASSWORD=    # aus .env.platform
npx playwright install chromium          # einmalig pro Maschine
E2E_BASE_URL=http://localhost:5174 make operator-ui-e2e

Gegen die integrierte Backend-Mount-Variante (nach Redeploy):

E2E_BASE_URL=http://localhost:8280/admin/operator make operator-ui-e2e