# Journal - 2026-03-09 ## 📝 Zusammenfassung Keycloak-Härtung der Realm-Konfiguration (`meldestelle-realm.json`). Aufbauend auf dem Infrastruktur-Fix vom 2026-03-06 (korrektes `start --optimized`, Healthcheck) wurden nun die Client-Konfigurationen und Sicherheitseinstellungen auf einen produktionsreifen Stand gebracht. ## 🛠️ Änderungen ### 1. `config/docker/keycloak/meldestelle-realm.json` #### Wildcard aus `webOrigins` entfernt - **Vorher:** `api-gateway` und `web-app` hatten `"*"` in `webOrigins` (CORS-Sicherheitslücke). - **Nachher:** Nur explizit erlaubte Origins (`localhost:*`, `app.meldestelle.at`). #### Neuer Client: `frontend-client` (KMP Desktop & Mobile) - **Public Client** mit **PKCE S256** (kein Client-Secret nötig, sicher für native Apps). - **Redirect URIs:** `meldestelle://callback` (Custom URI Scheme für Desktop), `http://localhost:*` (Dev), `https://app.meldestelle.at/*` (Prod). - **Kein `directAccessGrantsEnabled`** — verhindert Password-Grant-Flow (unsicher für native Apps). #### Password-Policy gestärkt - **Vorher:** `length(8)` — zu schwach. - **Nachher:** `length(10) and digits(1) and upperCase(1) and specialChars(1) and notUsername(undefined)`. #### `post.logout.redirect.uris` konfiguriert - Alle relevanten Clients (`api-gateway`, `web-app`, `frontend-client`) haben nun korrekte Logout-Redirect-URIs gemäß OIDC Back-Channel Logout Standard. ## 📚 Gelerntes / Entscheidungen - **`meldestelle://callback`** als Custom URI Scheme: Für KMP Compose Desktop ist ein eigenes URI-Schema der sichere Standard (kein offener HTTP-Port nötig). Muss im OS registriert werden. - **PKCE S256 ist Pflicht für Public Clients:** Verhindert Authorization Code Interception Attacks. Keycloak `26.x` unterstützt dies nativ. - **`frontend-client` vs. `web-app`:** Klare Trennung: `web-app` für Browser-basierte Web-App, `frontend-client` für native KMP Desktop/Mobile-App. ## ✅ Micrometer Upgrade (2026-03-09, gleiche Session) Verifiziert: `micrometer = "1.16.1"` und `micrometerTracing = "1.6.1"` waren bereits korrekt in `gradle/libs.versions.toml` gesetzt — kein Code-Change erforderlich. Archiv-Roadmaps entsprechend abgehakt. ## ✅ Zipkin Integration (2026-03-09, gleiche Session) Analyse ergab: Infrastruktur war bereits zu ~90% korrekt aufgebaut. Einzige Lücke war eine falsche Propagation-Konfiguration im Gateway. ### Befund - `dc-infra.yaml`: Zipkin-Container (`openzipkin/zipkin:3`, Port 9411) bereits vorhanden. - `monitoring-client`-Bundle: enthält `micrometer-tracing-bridge-brave`, `zipkin-reporter-brave`, `zipkin-sender-okhttp3` — von allen Services eingebunden. - `monitoring-defaults.properties`: setzt `management.zipkin.tracing.endpoint=http://localhost:9411/api/v2/spans` als Default, überschreibbar via `MANAGEMENT_ZIPKIN_TRACING_ENDPOINT`. - `dc-backend.yaml`: setzt `MANAGEMENT_ZIPKIN_TRACING_ENDPOINT=http://zipkin:9411/api/v2/spans` für alle Services inkl. Gateway. ### Fix: Gateway `application.yaml` - **`management.tracing.propagation.type`**: `w3c` → `b3` (B3 ist das native Format von Brave/Zipkin; W3C wäre für OpenTelemetry — Mismatch hätte Trace-Korrelation zwischen Gateway und Services gebrochen). - **`management.zipkin.tracing.endpoint`**: Explizit mit lokalem Default + ENV-Override ergänzt. - **`TRACING_SAMPLING_PROBABILITY`**: Konsistent mit Services via ENV-Variable. ### Fix: Gateway `build.gradle.kts` - Redundante direkte `micrometer-tracing-bridge-brave`-Dependency entfernt (bereits transitiv via `monitoring-client` vorhanden). ## ✅ OIDC Client im Frontend (2026-03-09, gleiche Session) Login-Flow mit PKCE Authorization Code Flow (S256) für `frontend-client` implementiert. ### Neue Dateien - **`frontend/core/auth/src/commonMain/.../Sha256.kt`**: Pure Kotlin SHA-256 (FIPS 180-4) + Base64URL-Encoding — kein expect/actual, läuft auf JVM/JS/Wasm. - **`frontend/core/auth/src/commonMain/.../PkceHelper.kt`**: Code Verifier, Code Challenge (S256), State Generator. - **`frontend/core/auth/src/commonMain/.../OidcCallback.kt`**: `OidcCallbackResult` sealed class + expect `launchOidcFlow()`, `consumePendingOidcCallback()`, `getOidcRedirectUri()`. - **`frontend/core/auth/src/jvmMain/.../OidcCallback.jvm.kt`**: Eingebetteter `HttpServer` (Port 18080) + `Desktop.browse()` + `CompletableDeferred` (Timeout 5 min). - **`frontend/core/auth/src/jsMain/.../OidcCallback.js.kt`**: `window.location.href` Redirect + URL-Parameter-Parsing beim App-Start + History-Bereinigung via `replaceState`. ### Geänderte Dateien - **`frontend/core/domain/.../AppConstants.kt`**: `KEYCLOAK_CLIENT_ID` → `frontend-client`, OIDC-Konstanten ergänzt. - **`frontend/core/auth/src/commonMain/.../AuthApiClient.kt`**: `buildAuthorizationUrl()` (PKCE URL-Builder) + `exchangeCodeForToken()` (Code → Token). - **`frontend/core/auth/src/commonMain/.../LoginViewModel.kt`**: `isOidcLoading`-State, `startOidcFlow()`, `handleOidcCallbackResult()`, JS-Init-Callback-Check. - **`frontend/core/auth/src/commonMain/.../LoginScreen.kt`**: Divider + `OutlinedButton` „Mit Keycloak anmelden" mit Spinner bei laufendem Flow. ### Architektur-Entscheidungen - **Kein `ktor-client-auth`**: Der OIDC-Flow wird manuell implementiert — `ktor-client-auth` unterstützt Authorization Code + PKCE nicht nativ für KMP. - **Pure Kotlin SHA-256**: Kein `expect/actual` nötig — `kotlin.math` + reine Bitoperationen reichen aus. - **JVM-Callback-Server** auf `localhost:18080`: Standard-Muster für Desktop-Apps (RFC 8252 „OAuth 2.0 for Native Apps"). - **JS-Redirect-Flow**: Kein Popup — volle Seitenweiterleitung. Code Verifier wird in `sessionStorage` gespeichert (nur aktueller Tab). - **State-Validierung**: CSRF-Schutz via State-Parameter-Vergleich im ViewModel. ## 🔜 Nächste Schritte - **TLS/HTTPS** — Langfristig: `KC_HOSTNAME_STRICT_HTTPS=true` setzen, sobald TLS eingerichtet ist. - **Gateway CircuitBreaker** — Verifizieren ob durch Spring Cloud 2025.0.1 bereits behoben.