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) imkora-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 Subdomainadmin.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-redirectUrisundwebOriginsentsprechend erweitern. - Realm-Default-Session-Lifetimes. Anders als die Vendor-Breakglass-
und -Tunnel-Clients (2h Client-Session-Max) bekommt
operator-uikeine 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-uibelegt Vite-Standard 5173;operator-uibekommt den nächsten freien Vite-Port, damit7.1bbeide 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.
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).
Für Neuinstallationen ist die nominelle Reihenfolge:
- Realm-JSON beim ersten Container-Start importieren (das passiert
automatisch via
kc.sh importim Keycloak-Bootstrap) - Init-Scripts in Reihenfolge
create-audit-service-account.sh,create-operator-ui-client.shausfü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):