Security by Default: Keycloak als zentrale Identität

Keycloak im Praxis-Setup: Realms, Clients, und warum wir auf shared-secrets verzichtet haben.

Authentifizierung ist ein Thema, das Entwickler nicht gerne anfassen. Zu spezifisch, zu riskant, zu langweilig. Das Ergebnis: In jedem Service findet sich eigene Auth-Logik, oft aus Tutorials zusammenkopiert, in unterschiedlichen Reifegraden. Wer jemals Auth-Bugs in der Produktion debuggt hat, weiß, wie teuer dieser Ansatz ist.

Warum Keycloak

Keycloak ist keine glänzende Neuheit, sondern ein ausgereiftes Projekt (mittlerweile 16 Jahre alt), das sich in Enterprise-Kontexten etabliert hat. Es spricht standardkonforme Protokolle: OIDC, SAML, Token Exchange. Es läuft in Kubernetes. Es lässt sich administrativ über UI, REST oder Admin-CLI steuern.

Alternativen gibt es: Authentik, Ory, oder kommerziell Auth0. Wir haben uns bewusst für das ausgereiftere, wenn auch etwas schwerere Keycloak entschieden — aus dem einfachen Grund, dass unsere Kunden in regulierten Branchen oft explizit nach Keycloak-Kompatibilität fragen.

Realms und Clients: Das Modell, das wir brauchen

Für jeden Tenant haben wir einen separaten Realm. Das ist wichtig, weil Keycloak-Realms echte Isolationsgrenzen sind — ein Compromise in einem Realm schadet anderen nicht. Innerhalb eines Realms leben alle Clients (Frontend-SPAs, Backend-Services, Machine-to-Machine-Accounts) und alle User.

Die User-Quelle ist pro Realm konfiguriert. Manche Tenants haben einen LDAP-Sync, andere einen Azure-AD-Provider, andere eine native User-Datenbank. Das Anwendungs-Layer sieht keinen Unterschied — es bekommt ein OIDC-Token und vertraut ihm.

Keine Shared Secrets

Ein häufiger Anti-Pattern: Services authentifizieren sich untereinander mit einem geteilten Secret, das in irgendeiner Umgebungs-Variablen lebt. Das skaliert nicht. Ein Leak bedeutet Rotation für alle Services. Ein neuer Service bedeutet Zugang zur Secret-Verwaltung.

Die saubere Alternative ist Service Account Tokens: Jeder Service hat einen Keycloak-Client mit einem eigenen Secret, holt sich ein Token, und sendet es beim API-Call. Der empfangende Service verifiziert das Token gegen Keycloak (oder via JWKS-Cache). Das Token ist kurzlebig (5 Minuten) und kann ohne großes Rollout rotiert werden.

PKCE für SPAs

Für Frontend-Anwendungen haben wir die klassischen Implicit-Flow- und Code-Flow-Optionen zugunsten von Authorization Code Flow + PKCE abgelöst. Der Unterschied: Keine Client-Secrets mehr im Browser, keine Tokens im URL-Fragment. Stattdessen erzeugt der Client einen kryptografischen Proof-of-Possession, den Keycloak gegen den initialen Request validiert.

Keycloak unterstützt PKCE nativ seit mehreren Jahren. Die Frontend-Bibliothek oidc-client-ts spielt perfekt mit. Der Migrationsaufwand lag bei 2 Personentagen pro SPA — überschaubar.

Lessons Learned

  • Token-Caching aggressiv betreiben. JWKS-Endpoints auf Keycloak sind günstig, aber bei 10k+ Requests/min wird das schmerzhaft. In-Memory-Caching mit 1h TTL ist ein guter Kompromiss.
  • Session-Timeouts bewusst wählen. Unser initialer Default — 30 Minuten Access Token, 8h Refresh Token — war zu großzügig. Für Administrativ-Tools haben wir das auf 15min/4h reduziert.
  • Logs sauber strukturieren. Keycloak produziert standardmäßig viel Noise. Ein gezielter Logback-Filter auf die Events, die uns interessieren (Login-Fehler, Token-Rotationen, Admin-Aktionen) hat unsere Grafana-Dashboards erst brauchbar gemacht.

Ausblick

Das nächste Thema ist delegierter Zugriff — Nutzer A darf Nutzer B begrenzte Rechte einräumen (z.B. "Makler sieht Policen seines Kunden"). Keycloak hat dafür das UMA-Protokoll, das sich in der Praxis aber als schwer zugänglich erwiesen hat. Wir evaluieren gerade, ob wir auf Keycloak Authorization Services setzen oder die Delegation in einem eigenen Service-Layer implementieren.