Blog · Architecture

Keycloak in Produktion

Vom WildFly-Erbe zur schlanken Quarkus-Distribution: was sich seit der großen Architektur-Migration geändert hat, wie ein produktives Cluster mit Infinispan und externer PostgreSQL aufgebaut ist, wie sich Themes für Login und E-Mail anpassen lassen, und wie Realm-Exports den Weg zu GitOps-fähiger IAM-Konfiguration freimachen. Ein zehnseitiger Tiefenartikel zu dem Werkzeug, das wir in unseren Kunden­projekten in regulierten Branchen am häufigsten antreffen.

Einordnung — das IAM für regulierte Branchen

Keycloak ist eine quelloffene Identity- und Access-Management-Plattform unter Apache-2.0-Lizenz, hervorgegangen aus einem JBoss-Projekt von Red Hat. Die kommerzielle Schwester unter dem Namen Red Hat build of Keycloak (vormals RH-SSO) wird in den allermeisten regulierten Setups in Deutschland eingesetzt — Banken, Versicherungen, Energieversorger und die öffentliche Verwaltung haben sich auf das Werkzeug verständigt, weil es self-hosted betrieben werden kann und alle gängigen Standards (OIDC, OAuth 2.0, SAML 2.0) abdeckt.

Drei Eigenschaften machen Keycloak in unseren Projekten zum De-facto-Standard. Erstens: self-hosted und EU-konform. Wo eine SaaS-Lösung wie Auth0 oder Okta an Datenschutz- und KRITIS-Anforderungen scheitert, läuft Keycloak im eigenen Rechenzentrum oder in der EU-Cloud. Zweitens: standardkonform und protokollreich. OIDC mit allen Flows (Authorization Code mit PKCE, Client Credentials, Device Code, CIBA), SAML als Identity Provider und Service Provider, dazu Identity Brokering zu externen IdPs und User Federation gegen LDAP oder Active Directory. Drittens: reif und gepflegt. Seit 2014 produktiv, hinter den Kulissen von Red Hat finanziell abgesichert, mit einer großen Open-Source-Community und monatlichen Patch-Releases.

Eine der wichtigsten Zäsuren in der Geschichte des Projekts liegt bei Version 17 (April 2022), mit der die Auslieferung von WildFly auf Quarkus umgestellt wurde. Die Quarkus-Distribution startet in unter zwei Sekunden, hat einen deutlich kleineren RAM-Footprint und konfiguriert sich über eine einzige Datei (keycloak.conf) oder Umgebungsvariablen — eine spürbare Vereinfachung gegenüber dem alten WildFly-Setup mit seinen XML-Subsystem-Dateien. Wer heute neu aufsetzt, sollte ausschließlich die Quarkus-Distribution einsetzen; die WildFly-Variante wird seit Version 20 nicht mehr unterstützt. Viele unserer Beratungs­einsätze beginnen genau an dieser Stelle: ein Bestandskunde hat noch die alte WildFly-Installation und braucht einen sauberen Migrationspfad.

Was Keycloak nicht ist, sollte ebenfalls klar benannt sein. Es ist keine Customer-Identity-Plattform mit fertigem Marketing-Tooling — wer ein Auth0 mit Drag-and-Drop-Login-Flows, A/B-Tests und vorgefertigten Social-Login-Buttons für ein Consumer-Produkt sucht, ist dort besser aufgehoben. Keycloak verlangt ein Mindestmaß an Engineering: Realm-Design, Theme-Anpassung, Cluster-Betrieb, Upgrade-Disziplin. Im Gegenzug bekommt man ein Werkzeug, das in der eigenen Hand bleibt und keine Vendor-Bindung erzeugt.

Kernkonzepte — Realm, Client, User, Rolle

Fünf Konzepte halten Keycloak zusammen. Wer sie unterscheidet, hat das mentale Modell — der Rest ist Konfiguration und Theme-Arbeit.

Realm — der Mandanten-Container

Ein Realm ist der oberste logische Container. Innerhalb eines Realms existieren Users, Clients, Rollen, Groups und Themes — vollständig isoliert von anderen Realms. Multi-Tenant-Setups bilden Mandanten typischerweise als eigene Realms ab: ein „mitarbeiter"-Realm für die interne Belegschaft, ein „kundenportal"-Realm für externe Endnutzer, gegebenenfalls je Geschäftsbereich oder Marke ein weiterer Realm. Der Master-Realm hat eine Sonderrolle: er verwaltet die Server-Konfiguration und die Realm-Administratoren — Endbenutzer haben dort nichts zu suchen.

Client — die registrierte Anwendung

Ein Client ist eine bei Keycloak registrierte Anwendung, die Authentifizierung in Anspruch nimmt. OIDC-Clients erhalten eine Client-ID und (für vertrauenswürdige Backend-Clients) ein Client-Secret. Vier wichtige Unterscheidungen: Public Clients (Browser-SPAs, Mobile-Apps — kein Secret, nutzen PKCE), Confidential Clients (Server-zu-Server, mit Secret oder mTLS), Bearer-Only-Clients (Resource Server, die nur Tokens validieren), und Service-Account-Clients (Machine-to-Machine ohne Endbenutzer). SAML-Clients gibt es zusätzlich für Legacy- oder B2B-Integrationen, in denen SAML weiterhin dominant ist.

User, Rolle, Group

Ein User ist eine Identität in einem Realm. Rollen sind die Berechtigungs­einheiten — sie gibt es auf zwei Ebenen: Realm Roles gelten realm-weit (etwa „realm-admin", „auditor"), Client Roles sind an einen konkreten Client gebunden (etwa „order-service.viewer", „order-service.editor"). Groups sind Sammlungen von Usern, die gemeinsame Attribute und Rollen erben. Eine Group „abteilung-bestellwesen" mit zwei zugewiesenen Client Roles vererbt diese automatisch an alle Mitglieder — der Standardweg für skalierbares Berechtigungsmanagement.

Composite Roles sind ein häufig übersehenes Feature: eine Rolle kann andere Rollen einschließen. „order-service.admin" als Composite Role kann „order-service.viewer" und „order-service.editor" implizit einschließen — Tokens des Users enthalten dann alle drei Rollen. Damit lassen sich Berechtigungs­hierarchien sauber modellieren, ohne dass auf Anwendungs­seite Rollenlogik nachgebaut werden muss.

Authentication Flow

Der Authentication Flow ist die konfigurierbare Kette von Schritten, die bei der Anmeldung durchlaufen wird: Username + Passwort → optional MFA → optional Risiko-Bewertung → Token-Ausgabe. Keycloak bringt Standard-Flows mit, die für 95 Prozent der Setups ausreichen — Anpassungen sind aber jederzeit möglich, etwa um eine zweistufige MFA-Pflicht für administrative Konten einzuführen, ohne andere User zu belasten.

Authentifizierungs-Flows in der Praxis

Welcher Flow für welche Anwendung gewählt wird, entscheidet später über Sicherheit und Wartbarkeit. Die richtige Wahl ist eine der wenigen wirklich folgenreichen Architektur-Entscheidungen rund um Keycloak.

Authorization Code mit PKCE — der Standard für interaktive Anwendungen

Egal ob klassische serverseitig gerenderte Web-App oder Single-Page-Anwendung: der Authorization Code Flow mit PKCE (Proof Key for Code Exchange) ist heute die richtige Wahl. Der ältere Implicit Flow ist nach OAuth 2.1 obsolet — wer ihn in alten Anwendungen findet, hat einen Migrationsfall vorliegen. PKCE schützt gegen Authorization-Code-Interception und ist auch für vertrauliche Clients empfehlenswert; in Public Clients (Browser, Mobile) ist er ohnehin Pflicht.

Client Credentials — Service-zu-Service

Wenn zwei Backend-Services miteinander reden und kein Endnutzer involviert ist, ist der Client Credentials Flow richtig. Der Service authentisiert sich mit seinem Client-Secret (oder mTLS-Zertifikat) und erhält ein Access Token mit den Berechtigungen seines Service-Accounts. In Microservice-Architekturen ein Brot-und-Butter-Pattern.

Device Authorization — TVs, Set-Top-Boxen, CLI-Tools

Geräte ohne Browser oder mit eingeschränkter Eingabe nutzen den Device Authorization Flow: der User bekommt einen kurzen Code angezeigt und gibt ihn auf einem anderen Gerät (Smartphone) ein, das den Browser-Login durchführt. Häufig auch für CLI-Tools genutzt — die az- oder gh-CLIs sind bekannte Beispiele aus dem nicht-Keycloak-Umfeld.

CIBA — Client-Initiated Backchannel Authentication

Eine im Banking-Umfeld zunehmend relevante Variante: der Endpunkt ist nicht der Benutzer-Browser, sondern eine Mitteilung an dessen Smartphone (Push-Notification mit Bestätigungs­dialog). CIBA ist Teil des FAPI-2.0-Profils und wird in PSD2/PSD3-konformen Banking-APIs künftig der Standard sein.

SAML 2.0 — Legacy und B2B

Trotz aller OIDC-Dominanz lebt SAML weiter — vor allem in B2B-Föderationen, im öffentlichen Sektor und in Enterprise-SaaS-Anbindungen. Keycloak fungiert als SAML Identity Provider (für externe Service Provider) und als SAML Service Provider (für ein externes Identity Provider). Die Konfiguration ist umfangreicher als bei OIDC; einmal eingerichtet, läuft sie aber stabil.

Cluster-Architektur

Ein produktives Keycloak-Setup besteht aus mehreren Komponenten, die zusammen die Hochverfügbarkeit, den geteilten Cache-Zustand und die persistente Datenhaltung tragen. Die folgende Abbildung zeigt die typische Topologie für eine Single-Site-Installation.

Keycloak — Cluster-TopologieSchaubild der Cluster-Topologie: Browser und OIDC-/SAML-Clients greifen über einen Reverse Proxy auf das Keycloak-Cluster zu. Das Cluster besteht aus drei Quarkus-Knoten, die ihren Cache (Sessions, Login-Fehlversuche, Realm-Cache) per Infinispan untereinander replizieren. Alle Knoten lesen und schreiben in eine externe PostgreSQL-Datenbank (HA-Setup). Ein optionaler LDAP- oder Active-Directory-Server dient als User-Federation-Quelle.Keycloak Cluster · Quarkus · Distributed InfinispanHTTPSHTTP · StickyJDBC · TCP 5432LDAPS · TCP 636Infinispan-Replikation aller KnotenBrowser & AnwendungenOIDC- und SAML-EndpunkteReverse ProxyTLS-TerminierungSticky SessionsKeycloak Node 1Quarkus · Java 17Embedded InfinispanSessions · Login FailuresRealm CacheKeycloak Node 2Quarkus · Java 17Embedded InfinispanSessions · Login FailuresRealm CacheKeycloak Node 3Quarkus · Java 17Embedded InfinispanSessions · Login FailuresRealm CachePostgreSQL (HA)Realm Config · UsersClients · Roles · GroupsJDBC Pool pro KnotenLDAP / Active DirectoryUser Federationread-only oder sync(optional)
Abbildung 1 — Drei-Knoten-Cluster mit Reverse Proxy, Infinispan-basierter Cache-Replikation und externer PostgreSQL als Konfigurations- und Identitäts­speicher. Optional dient ein LDAP- oder Active-Directory-Server als User-Federation-Quelle. Jeder Knoten hält einen synchronisierten Cache; bei Ausfall eines Knotens leiten Reverse Proxy und Infinispan die Sessions automatisch auf die verbleibenden Knoten um.

Was Infinispan tatsächlich speichert

Die Cache-Schicht in Keycloak hält drei Arten von Daten: Realm-Cache (Realm-Definitionen, Client-Konfigurationen — selten geändert, häufig gelesen), Session-Cache (aktive User-Sessions, Refresh-Tokens, Authorization Codes — zentral für Stateful-Verhalten), und Login-Failures (für Brute-Force-Schutz). Die ersten beiden werden in einem distributed-Cache-Modus über alle Knoten verteilt; jede Information liegt auf mehreren Knoten und überlebt einen Knoten-Ausfall.

Stickiness am Reverse Proxy

Auch wenn der Infinispan-Cluster jede Session auf jedem Knoten verfügbar macht, hat sich in der Praxis Session-Affinität bewährt — der Reverse Proxy leitet einen User möglichst immer auf denselben Knoten. Performance-Gründe (Cache-Hit-Raten) sind der eine Aspekt; der andere ist die Stabilität bei längeren OIDC-Flows mit mehreren Schritten (Login, MFA, Consent), die auf einem konsistenten Knoten reibungsloser laufen.

Cross-Datacenter

Für Multi-Site-Setups bietet Keycloak einen Multi-Site-Mode mit aktiv-aktiv-Replikation über zwei Rechenzentren. Der Aufbau ist anspruchsvoller — Infinispan über RPC, Postgres-Replikation, abgestimmtes Failover-Verhalten. In den meisten Tenvias-Projekten ist ein Single-Site-HA-Setup mit drei Knoten ausreichend; Cross-DC wird konkret beim Banking-Kunden mit echtem Disaster-Recovery-Anspruch relevant.

Installation und Konfiguration der Quarkus-Distribution

Eine produktionsnahe Konfiguration besteht aus drei Bausteinen: der Quarkus-Distribution, einer externen PostgreSQL-Datenbank und einer durchdachten Konfigurations­datei. Die folgenden Schritte zeigen ein lauffähiges Single-Node-Setup, das sich direkt zum HA-Cluster ausbauen lässt.

Voraussetzungen

Java 17 oder neuer, ein externer PostgreSQL-Server (mindestens 13, idealerweise 15+), ein FQDN mit gültigem TLS-Zertifikat hinter einem Reverse Proxy. Die mitgelieferte kc.sh-Datei ist das Standard-Werkzeug für Start, Build und Migration.

Konfigurationsdatei keycloak.conf

Alle Einstellungen leben in conf/keycloak.conf. Die wichtigsten Blöcke:

# --- Datenbank ---
db=postgres
db-url=jdbc:postgresql://pg-primary.intern.example.de:5432/keycloak
db-username=keycloak
db-password=<via env: KC_DB_PASSWORD>
db-pool-min-size=10
db-pool-max-size=50

# --- Hostname und Reverse Proxy ---
hostname=auth.intern.example.de
hostname-strict-https=true
proxy-headers=xforwarded
http-enabled=true   # TLS terminiert der Reverse Proxy

# --- Cache (Infinispan) ---
cache=ispn
cache-stack=kubernetes   # oder: tcp / udp / ec2 / google

# --- Health- und Metrics-Endpunkte ---
health-enabled=true
metrics-enabled=true

# --- Logging ---
log=console,file
log-level=INFO
log-console-format="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"

Sensible Werte (Passwörter, Client Secrets) gehören nicht in die Datei, sondern in Umgebungs­variablen mit dem KC_-Präfix. db-password wird so über KC_DB_PASSWORD gesetzt. In Kubernetes typischerweise als Secret eingebunden.

Build-Optimierung

Eine Eigenheit der Quarkus-Distribution: vor dem produktiven Start gehört ein kc.sh build-Schritt, der die statischen Einstellungen (Datenbanktreiber, aktivierte Features) in eine optimierte Distribution kompiliert. Wer das vergisst, sieht beim Start eine Warnung und einen langsameren Boot. In Container-Images macht man das einmal beim Image-Bauen:

FROM quay.io/keycloak/keycloak:26.0 as builder
ENV KC_DB=postgres
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:26.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
CMD ["start", "--optimized"]

Initialer Admin-User

Beim allerersten Start sucht Keycloak einen Bootstrap-Admin in den Umgebungs­variablen KC_BOOTSTRAP_ADMIN_USERNAME und KC_BOOTSTRAP_ADMIN_PASSWORD. Direkt nach dem ersten Login wird ein regulärer Admin-User angelegt und der Bootstrap-Pfad deaktiviert. In Produktion sollte das initiale Passwort nach genau diesem Schritt rotiert werden.

Kubernetes mit Operator

Für Kubernetes-Setups ist der offizielle Keycloak Operator der etablierte Weg. Er übernimmt das Cluster-Scaling, die Postgres-Verbindung und die Realm-Konfiguration über CRDs. Eine Minimal-Definition:

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
  name: auth
  namespace: identity
spec:
  instances: 3
  db:
    vendor: postgres
    host: pg-primary.identity.svc.cluster.local
    database: keycloak
    usernameSecret:
      name: keycloak-db
      key: username
    passwordSecret:
      name: keycloak-db
      key: password
  hostname:
    hostname: auth.example.de
  proxy:
    headers: xforwarded
  ingress:
    enabled: true

Der Operator startet drei Keycloak-Pods mit Infinispan-Discovery über das Kubernetes-Stack, lädt das DB-Passwort aus dem Secret und bindet alles an eine Ingress-Definition. Realm-Konfigurationen lassen sich über die zusätzliche CRD KeycloakRealmImport deklarativ pflegen — der Weg zu GitOps-fähigem IAM.

Themes und Custom Branding

Eines der unterschätzten Themen in Keycloak: das mitgelieferte Login-Frontend sieht aus wie Keycloak — und das ist in den meisten Kunden­projekten nicht das, was die Geschäfts­seite akzeptiert. Themes erlauben ein vollständiges Re-Branding ohne Eingriff in den Server-Code.

Theme-Typen

Keycloak kennt vier Theme-Typen, die alle separat angepasst werden können: login (Login-Seiten, Passwort-Reset, Consent), account (die Account-Konsole, in der Endbenutzer ihr eigenes Profil verwalten), email (E-Mail-Vorlagen für Bestätigungs- und Reset-Mails), und admin (die Admin-Konsole — wird in produktiven Setups praktisch nie angepasst). Für ein Kunden-Branding reichen in den meisten Fällen login und email aus.

Theme-Verzeichnisstruktur

Ein Theme lebt im Verzeichnis themes/<theme-name>/<typ>/ mit einer klaren Substruktur:

themes/
  tenvias/
    login/
      theme.properties
      messages/
        messages_de.properties
        messages_en.properties
      resources/
        css/
          login.css
        img/
          logo.svg
      template.ftl
      login.ftl
      login-update-password.ftl
    email/
      theme.properties
      html/
        email-verification.ftl
        password-reset.ftl
      text/
        email-verification.ftl
        password-reset.ftl

Die theme.properties jedes Theme-Typs erlaubt es, von einem Parent-Theme zu erben — typischerweise parent=keycloak. Damit überschreibt das eigene Theme nur die Dateien, die sich tatsächlich unterscheiden, und behält den Standard-Fallback für den Rest. Wer ein Login-Theme „from scratch" bauen will, sollte die nächste Stunde planen und dann doch auf die Parent-Vererbung umstellen — die Anzahl der zu überschreibenden FTL-Templates ist größer als gedacht.

FreeMarker als Template-Engine

Die Templates sind in FreeMarker (.ftl) geschrieben. Wer FreeMarker noch nicht kennt: die Syntax liegt zwischen JSP und Thymeleaf; ${...} für Variablen, <#if ...> für Bedingungen, <#list ...> für Schleifen. Die wichtigste Datei eines Login-Themes ist template.ftl, die das Grundgerüst (Header, Footer, Logo, CSS-Includes) definiert; die einzelnen Seiten (login.ftl, register.ftl, login-update-password.ftl) erben davon.

Theme-Deployment

Im klassischen Modell legt man das Theme-Verzeichnis unter themes/ ab und startet Keycloak neu. Für GitOps-Setups verpackt man das Theme als JAR mit einer META-INF/keycloak-themes.json und kopiert es in das providers/-Verzeichnis — mit einem kc.sh build wird es registriert. Das ist auch der Weg, der mit dem Keycloak Operator zusammenarbeitet, weil das Theme als Container-Layer im Image landet.

Praxis-Hinweis

Halten Sie Theme-Anpassungen minimal. Jedes überschriebene FTL-Template bindet Sie an eine Keycloak-Version — bei Major-Upgrades kann sich die zugrundeliegende Template-Struktur ändern, und Ihr Theme muss nachgezogen werden. Wer nur CSS und ein paar Texte anpasst, kommt fast verlustfrei durch Upgrades. Wer ganze HTML-Strukturen umgeschrieben hat, wird das bei jedem Major-Schritt bereuen.

Integration in Anwendungen

Die Anbindung an Keycloak ist auf der Anwendungsseite überraschend einfach, sobald die richtigen Bibliotheken eingesetzt werden. Die schwierigeren Themen liegen bei Reverse-Proxy-Headern und der Token-Validierung.

Spring Boot — Resource Server

Ein Spring-Boot-Backend, das Tokens validiert, braucht den OAuth2-Resource-Server-Starter und eine einzige Konfigurationszeile:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.intern.example.de/realms/mitarbeiter

Das JWKS-Endpoint wird automatisch über die OIDC-Discovery (.well-known/openid-configuration) gefunden. Token-Signaturen werden gegen die öffentlichen Schlüssel von Keycloak validiert; bei Schlüsselrotation lädt Spring die neuen Schlüssel automatisch nach. In der Java-Konfiguration werden anschließend Rollen aus dem Token gemappt (typischerweise aus realm_access.roles oder resource_access.<client>.roles).

Frontend — Single-Page-Anwendung

Für moderne Frontends (React, Vue, Angular) ist die Library oidc-client-ts der Standardweg. Sie übernimmt den Authorization-Code-Flow mit PKCE, das Token-Refreshing und die Logout-Logik:

import { UserManager, WebStorageStateStore } from "oidc-client-ts";

const userManager = new UserManager({
  authority:     "https://auth.intern.example.de/realms/mitarbeiter",
  client_id:     "mitarbeiter-portal",
  redirect_uri:  window.location.origin + "/callback",
  response_type: "code",
  scope:         "openid profile email roles",
  loadUserInfo:  true,
  userStore: new WebStorageStateStore({ store: window.localStorage }),
});

await userManager.signinRedirect();

Wichtig: PKCE ist in oidc-client-ts standardmäßig aktiv — kein Client Secret im Browser. Das ist genau die richtige Voreinstellung; alle Versuche, ein Client Secret aus einem Browser zu nutzen, sind sicherheitstechnisch sinnlos.

Reverse Proxy und X-Forwarded-Header

Eine der häufigsten Stolpersteine: Keycloak hinter einem Reverse Proxy „sieht" sich selbst auf der internen HTTP-URL, die Browser sehen ihn aber unter der externen HTTPS-URL. Wer das nicht regelt, baut OIDC-Redirect-URLs zusammen, die der Browser ablehnt. Drei Konfigurationen müssen zusammenspielen:

  • Keycloak: proxy-headers=xforwarded, hostname=auth.intern.example.de, hostname-strict-https=true in keycloak.conf.
  • Reverse Proxy (Nginx): proxy_set_header X-Forwarded-Proto https; und X-Forwarded-Host, X-Forwarded-For entsprechend setzen.
  • Container-Netzwerk: die internen Adressen müssen für die Trusted-Proxy-Liste freigeschaltet sein, damit Keycloak die Header überhaupt akzeptiert.

Sobald diese drei Stellen aufeinander abgestimmt sind, ist der Rest reibungsfrei. Wenn nicht, sieht man stundenlang Redirect-Fehler ohne erkennbaren Grund.

Federation, Brokering und FAPI

Über die Standard-Anwendungsfälle hinaus bietet Keycloak drei Erweiterungsbereiche, die in regulierten Branchen regelmäßig zum Tragen kommen.

Identity Brokering — fremde IdPs als Login-Quelle

Identity Brokering bedeutet: Keycloak delegiert die eigentliche Authentifizierung an einen anderen IdP, übernimmt aber die Identität und Token-Ausgabe nach eigenen Regeln. Klassische Beispiele: Login per Google, GitHub oder Microsoft Entra ID für externe Mitarbeitende; BundID oder ELSTER-Login für Anwendungen im öffentlichen Sektor; Microsoft Entra ID als Föderation für M365-Konten. Keycloak ist Service Provider und übersetzt die Antwort des externen IdP in eigene Tokens — die Anwendung dahinter bekommt einheitliche Tokens, unabhängig davon, woher der User ursprünglich kam.

User Federation — externe Identitätsspeicher

User Federation ist ein anderes Konzept: hier liegt die Benutzerdatenbank in einem externen System (LDAP oder Active Directory), Keycloak greift darauf zu, um Authentifizierungen durchzuführen. Drei Modi: read-only (Keycloak liest, schreibt nichts zurück — der LDAP-Server bleibt die alleinige Quelle der Wahrheit), writable (Änderungen am User in Keycloak werden in das LDAP zurückgeschrieben), import (Users werden einmal importiert und ab dann lokal verwaltet). In den allermeisten Tenvias-Projekten ist read-only die richtige Wahl — das AD bleibt die führende Identitätsquelle, Keycloak ergänzt nur die OIDC-/SAML-Sicht darüber.

FAPI — Financial-Grade API

FAPI 1.0 Advanced und FAPI 2.0 sind Sicherheitsprofile des OpenID-Konsortiums, die Banking-APIs unter PSD2 und PSD3 erfüllen müssen. Keycloak unterstützt FAPI über vorkonfigurierte Client-Profile, die strenge Mindestanforderungen erzwingen: PAR (Pushed Authorization Requests), DPoP oder mTLS für Sender-Constrained-Tokens, asymmetrische Client-Authentifizierung über Private-Key-JWT statt Client Secret. Wer eine Banking-API anbietet, kommt um FAPI nicht herum — und Keycloak ist eines der wenigen Open-Source-IdPs, das die Anforderungen vollständig abdeckt.

Customizing in der Praxis — Schritt für Schritt

Zwei Anpassungen tauchen in praktisch jedem Keycloak-Projekt auf, das wir begleiten: die Umgestaltung der Login-Seite an das Kunden-Branding und die Anbindung einer externen User-Quelle, die weder LDAP noch Active Directory ist. Beide Aufgaben lassen sich sauber lösen, sobald man den Werkzeugkasten verstanden hat. Die folgenden zwei Anleitungen führen Schritt für Schritt durch — die erste mit FreeMarker und CSS, die zweite mit Java-Code für einen eigenen Federation-Provider.

(a) Login-Seite umgestalten

Die mitgelieferte Keycloak-Login-Maske wirkt unverkennbar nach Keycloak. Für Kundenprojekte ist das fast nie akzeptabel — die folgenden sieben Schritte führen zu einer vollständig gebrandedten Variante mit eigenem Logo, eigenen Farben, eigenen Texten und optional einem zusätzlichen Hinweisblock im Formular.

Schritt 1 — Theme-Verzeichnis anlegen. Im Keycloak-Server (oder als Maven-Modul, falls das Theme als JAR ausgeliefert wird) wird folgende Struktur aufgebaut:

themes/tenvias-login/
  login/
    theme.properties
    login.ftl
    resources/
      css/
        custom.css
      img/
        logo.svg
    messages/
      messages_de.properties
      messages_en.properties

Schritt 2 — theme.properties mit Parent-Vererbung. Diese Datei steuert, was vom mitgelieferten Theme geerbt wird und welche eigenen CSS-Dateien geladen werden:

parent=keycloak
import=common/keycloak
styles=css/login.css css/custom.css
locales=de,en

parent=keycloak heißt: alle nicht überschriebenen Templates und Ressourcen werden aus dem mitgelieferten Keycloak-Theme geerbt. Die styles-Zeile listet die CSS-Dateien in der gewünschten Reihenfolge — wichtig ist, die mitgelieferte login.css zuerst und die eigene custom.css danach zu laden, damit die eigenen Regeln die Defaults überschreiben.

Schritt 3 — Brand-spezifisches CSS. Die Datei resources/css/custom.css enthält die Branding-Anpassungen. Ein typisches Beispiel:

/* Brand-Farben aus den Tenvias-Design-Tokens */
:root {
    --brand-primary: #2c3645;
    --brand-accent:  #4a7bc8;
    --brand-bg:      #f7f8fb;
}

body.login-pf {
    background:  var(--brand-bg);
    font-family: 'Inter', system-ui, sans-serif;
}

.login-pf-page .login-pf-header {
    background-image:    url(../img/logo.svg);
    background-size:     240px auto;
    background-position: center;
    background-repeat:   no-repeat;
    height:              80px;
    margin-bottom:       32px;
}

.login-pf-page .card-pf {
    border-radius: 12px;
    box-shadow:    0 4px 24px rgba(0, 0, 0, 0.08);
    padding:       32px;
    border:        1px solid #e5e8ee;
}

.login-pf-page .btn-primary {
    background:    var(--brand-primary);
    border-color:  var(--brand-primary);
    border-radius: 8px;
    padding:       10px 24px;
    font-weight:   600;
}

.login-pf-page .btn-primary:hover {
    background: var(--brand-accent);
}

Schritt 4 — Eigene Texte und Übersetzungen. Die Standard-Beschriftungen (etwa „Sign In") sollen durch eigene Begriffe ersetzt werden. In messages/messages_de.properties:

loginAccountTitle=Anmeldung bei Tenvias
doLogIn=Anmelden
usernameOrEmail=Benutzername oder E-Mail
password=Passwort
doForgotPassword=Passwort vergessen?

hinweisTitel=Hinweis zur Anmeldung
hinweisText=Diese Anwendung ist nur fuer berechtigte Mitarbeitende. \
            Bei Fragen wenden Sie sich an servicedesk@tenvias.de.

Die Schlüsselnamen loginAccountTitle, doLogIn und so weiter sind in Keycloak vordefiniert; eine vollständige Liste findet sich im Quellcode des Keycloak-Standard-Themes. Eigene Schlüssel (im Beispiel hinweisTitel und hinweisText) können in eigenen FreeMarker-Templates über ${msg("hinweisTitel")} verwendet werden.

Schritt 5 — Selektives Überschreiben einer FreeMarker-Datei. Wenn das Standard-Layout nicht reicht, kann jede .ftl-Datei aus dem Parent-Theme einzeln überschrieben werden. Eine angepasste login.ftl, die einen zusätzlichen Hinweisblock über dem Login-Formular einfügt:

<#import "template.ftl" as layout>
<@layout.registrationLayout
    displayMessage=!messagesPerField.existsError('username','password')
    displayInfo=realm.password && realm.registrationAllowed; section>

    <#if section = "header">
        ${msg("loginAccountTitle")}

    <#elseif section = "form">
        <div class="alert-info"
             style="padding:16px; margin-bottom:24px;
                    border-left:4px solid var(--brand-accent);
                    background:#eef3fb; border-radius:6px;">
            <strong>${msg("hinweisTitel")}</strong>
            <p style="margin:8px 0 0 0;">${msg("hinweisText")}</p>
        </div>

        <form id="kc-form-login"
              onsubmit="login.disabled = true; return true;"
              action="${url.loginAction}" method="post">
            <!-- Standard-Formularinhalt aus dem Parent-Theme uebernehmen -->
        </form>
    </#if>
</@layout.registrationLayout>

Nur die Sektionen, die wirklich überschrieben werden sollen, müssen ausgeschrieben werden — alles andere lebt im Parent-Template weiter.

Schritt 6 — Theme deployen. Bei einem klassischen Setup wird das Verzeichnis nach ${KEYCLOAK_HOME}/themes/ kopiert und Keycloak neu gestartet. Bei einem Container- oder Kubernetes-Setup wird das Theme als JAR verpackt — Verzeichnisstruktur im JAR:

src/main/resources/
  META-INF/keycloak-themes.json
  theme/tenvias-login/login/...

Die Datei META-INF/keycloak-themes.json registriert das Theme bei Keycloak:

{
  "themes": [
    {
      "name":  "tenvias-login",
      "types": ["login"]
    }
  ]
}

JAR ins providers/-Verzeichnis legen, anschließend kc.sh build ausführen und Keycloak neu starten.

Schritt 7 — Theme im Realm aktivieren. In der Admin-Konsole: Realm Settings → Themes → Login Theme auf tenvias-login stellen. Alternativ deklarativ über den Realm-JSON-Export:

{
  "realm":      "mitarbeiter",
  "loginTheme": "tenvias-login",
  "...":        "..."
}

Der nächste Login-Aufruf zeigt die angepasste Maske. Falls die Änderungen nicht sichtbar werden, hilft fast immer ein Browser-Cache-Reload oder das Setzen von spi-theme-cache-themes=false während der Theme-Entwicklung.

(b) Externe Quelle über einen Federation-Provider anbinden

Wenn weder LDAP noch Active Directory die existierende User-Datenbank abdecken — typisch bei Legacy-Anwendungen mit eigener User-Tabelle oder einer eigenen REST-API — schreibt man einen eigenen User Storage Provider in Java. Das folgende Beispiel bindet eine externe REST-API als Federation-Quelle an: Keycloak fragt bei jedem Login die Legacy-API ab, validiert das Passwort dort und stellt die Identität als „normalen" Keycloak-User dar. Wir gehen die sieben notwendigen Schritte durch.

Schritt 1 — Maven-Projekt aufsetzen. Eine pom.xml mit den Keycloak-SPI-Abhängigkeiten als provided:

<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>de.tenvias.keycloak</groupId>
    <artifactId>legacy-rest-storage-provider</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <keycloak.version>26.0.0</keycloak.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-core</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi-private</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

scope=provided ist entscheidend: die SPI-Bibliotheken bringt Keycloak selbst mit, sie dürfen nicht in das ausgelieferte JAR gebündelt werden, sonst entstehen Class-Loader-Konflikte zur Laufzeit.

Schritt 2 — Provider-Klasse implementieren. Die zentrale Klasse implementiert UserStorageProvider und die Schnittstellen, die das jeweilige Feature abdecken. Für Username-Lookup und Passwort-Validierung sind das UserLookupProvider und CredentialInputValidator:

package de.tenvias.keycloak;

import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.CredentialInputValidator;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.user.UserLookupProvider;

public class LegacyRestStorageProvider
        implements UserStorageProvider,
                   UserLookupProvider,
                   CredentialInputValidator {

    private final KeycloakSession session;
    private final ComponentModel   model;
    private final LegacyApiClient  apiClient;

    public LegacyRestStorageProvider(KeycloakSession session,
                                     ComponentModel  model,
                                     LegacyApiClient apiClient) {
        this.session   = session;
        this.model     = model;
        this.apiClient = apiClient;
    }

    @Override
    public UserModel getUserByUsername(RealmModel realm, String username) {
        LegacyApiClient.User legacyUser = apiClient.findByUsername(username);
        if (legacyUser == null) return null;
        return new LegacyUserAdapter(session, realm, model, legacyUser);
    }

    @Override
    public UserModel getUserByEmail(RealmModel realm, String email) {
        LegacyApiClient.User legacyUser = apiClient.findByEmail(email);
        if (legacyUser == null) return null;
        return new LegacyUserAdapter(session, realm, model, legacyUser);
    }

    @Override
    public UserModel getUserById(RealmModel realm, String id) {
        String externalId = StorageId.externalId(id);
        return getUserByUsername(realm, externalId);
    }

    @Override
    public boolean supportsCredentialType(String credentialType) {
        return PasswordCredentialModel.TYPE.equals(credentialType);
    }

    @Override
    public boolean isConfiguredFor(RealmModel realm, UserModel user,
                                   String credentialType) {
        return supportsCredentialType(credentialType);
    }

    @Override
    public boolean isValid(RealmModel realm, UserModel user,
                           CredentialInput input) {
        if (!supportsCredentialType(input.getType())) return false;
        return apiClient.authenticate(
            user.getUsername(),
            input.getChallengeResponse());
    }

    @Override
    public void close() {
        apiClient.close();
    }
}

Die Methode isValid ist der eigentliche Sicherheits-Anker: hier wird das vom Browser übergebene Passwort an die Legacy-API weitergereicht, die ihrerseits ein true oder false zurückgibt. Keycloak speichert das Passwort nicht — bei jedem Login wird die Legacy-API erneut befragt.

Schritt 3 — Adapter-Klasse für das User-Modell. Keycloak erwartet ein UserModel. Die meisten Felder werden über die Basisklasse AbstractUserAdapterFederatedStorage automatisch in Keycloak gehalten (Profilfelder, Rollen-Zuweisungen, Group-Mitgliedschaften). Wir überschreiben nur Username, Email, Vor- und Nachname aus der externen Quelle:

package de.tenvias.keycloak;

import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;

public class LegacyUserAdapter extends AbstractUserAdapterFederatedStorage {

    private final LegacyApiClient.User legacyUser;

    public LegacyUserAdapter(KeycloakSession session,
                             RealmModel     realm,
                             ComponentModel model,
                             LegacyApiClient.User legacyUser) {
        super(session, realm, model);
        this.legacyUser = legacyUser;
        this.storageId  = new StorageId(model.getId(),
                                        legacyUser.getUsername()).getId();
    }

    @Override
    public String getUsername()  { return legacyUser.getUsername(); }
    @Override
    public String getEmail()     { return legacyUser.getEmail();    }
    @Override
    public String getFirstName() { return legacyUser.getFirstName(); }
    @Override
    public String getLastName()  { return legacyUser.getLastName();  }

    @Override
    public void setUsername(String username) {
        throw new UnsupportedOperationException(
            "Read-only Federation gegen Legacy-API");
    }
}

Read-Only-Federation heißt: Profil­änderungen, die ein User in der Account-Konsole von Keycloak macht, werden nicht in die Legacy-API zurückgeschrieben. Wer das umgekehrte Verhalten will, implementiert die Setter-Methoden mit echten API-Aufrufen.

Schritt 4 — ProviderFactory. Die Factory ist der Einstiegspunkt für Keycloak. Sie definiert die Konfigurations­parameter, die der Administrator in der UI sieht, und erzeugt Provider-Instanzen pro Request:

package de.tenvias.keycloak;

import java.util.List;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.storage.UserStorageProviderFactory;

public class LegacyRestStorageProviderFactory
        implements UserStorageProviderFactory<LegacyRestStorageProvider> {

    public static final String PROVIDER_ID = "legacy-rest-storage";

    private static final List<ProviderConfigProperty> CONFIG_PROPERTIES =
        ProviderConfigurationBuilder.create()
            .property()
                .name("apiUrl")
                .label("Legacy API URL")
                .helpText("Basis-URL der externen REST-API")
                .type(ProviderConfigProperty.STRING_TYPE)
                .defaultValue("https://legacy.intern.example.de/api")
                .add()
            .property()
                .name("apiToken")
                .label("API Bearer Token")
                .helpText("Token fuer die Authentifizierung gegen die API")
                .type(ProviderConfigProperty.PASSWORD)
                .secret(true)
                .add()
            .property()
                .name("connectTimeoutMs")
                .label("Connect Timeout (ms)")
                .type(ProviderConfigProperty.STRING_TYPE)
                .defaultValue("3000")
                .add()
            .build();

    @Override
    public String getId() {
        return PROVIDER_ID;
    }

    @Override
    public String getHelpText() {
        return "User-Federation gegen eine externe REST-API (Tenvias)";
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        return CONFIG_PROPERTIES;
    }

    @Override
    public LegacyRestStorageProvider create(KeycloakSession session,
                                            ComponentModel  model) {
        String apiUrl   = model.get("apiUrl");
        String apiToken = model.get("apiToken");
        int    timeout  = Integer.parseInt(
                              model.get("connectTimeoutMs", "3000"));
        LegacyApiClient client = new LegacyApiClient(apiUrl, apiToken, timeout);
        return new LegacyRestStorageProvider(session, model, client);
    }
}

Der Typ ProviderConfigProperty.PASSWORD sorgt dafür, dass das API-Token in der Admin-Konsole maskiert dargestellt und im Realm-JSON-Export entsprechend behandelt wird — in Setups mit aktiviertem Vault landet dort nur die Vault-Referenz, nicht der Klartext.

Schritt 5 — SPI-Registrierung über die Services-Datei. Damit Keycloak die Factory beim Start findet, wird eine META-INF/services/-Datei mit dem Klassennamen erstellt:

src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory

Inhalt — eine einzige Zeile mit dem voll qualifizierten Klassennamen der Factory:

de.tenvias.keycloak.LegacyRestStorageProviderFactory

Wer mehrere Factories in einem Modul ausliefert, listet sie zeilenweise untereinander.

Schritt 6 — Bauen und Deployen. Der JAR wird per Maven gebaut und ins providers/-Verzeichnis der Keycloak-Installation kopiert. Anschließend muss Keycloak rebuilden, damit der Provider in die optimierte Distribution aufgenommen wird:

# JAR bauen
mvn clean package

# JAR ins providers-Verzeichnis legen
cp target/legacy-rest-storage-provider-1.0.0.jar \
   /opt/keycloak/providers/

# Keycloak rebuilden (registriert den neuen Provider)
/opt/keycloak/bin/kc.sh build

# Starten
/opt/keycloak/bin/kc.sh start

In Container-Setups ist der typische Weg ein Multi-Stage-Dockerfile: eine Maven-Stage baut den JAR, eine zweite Stage kopiert ihn in das offizielle Keycloak-Image und ruft direkt im Build kc.sh build auf. Damit ist der Provider Teil des Image-Inventars und reisefertig.

Schritt 7 — Provider im Realm aktivieren. In der Admin-Konsole: Realm → User Federation → Add Provider. In der Auswahl erscheint legacy-rest-storage — die in der Factory definierten Konfigurations­parameter (URL, Token, Timeout) erscheinen als Eingabefelder. Nach dem Speichern ist die Federation aktiv. Der nächste Login mit einem Username, der in der Legacy-API existiert, durchläuft den vollständigen Flow: getUserByUsername → Provider liefert einen LegacyUserAdapterisValid validiert das Passwort gegen die Legacy-API → Keycloak stellt einen Token an die anfragende Anwendung aus.

Praxis-Hinweis

Den hier ausgeklammerten LegacyApiClient sollten Sie nicht naiv mit dem JDK-HttpClient und einer Schleife implementieren. In Produktion gehören vier Eigenschaften zwingend dazu: Connection-Pooling (sonst öffnet Keycloak pro Login eine neue TCP-Verbindung), Caching auf User-Lookups mit kurzer TTL von 30–60 Sekunden (entlastet die Legacy-API spürbar), ein Circuit Breaker über Resilience4j (damit eine ausgefallene Legacy-API nicht die gesamte Anmeldeschleife in Keycloak blockiert) und Tracing-Header-Propagation, damit ein Vorfall über das Log-System (siehe unseren Artikel zu Logging im Enterprise-Umfeld) korrelierbar bleibt.

Realm-Lifecycle, Backup und Zero-Downtime-Upgrades

Die laufende Pflege ist der operative Teil, der über Erfolg oder Misserfolg eines IAM entscheidet. Drei Disziplinen sind die wichtigsten.

Realm-Export und -Import als Code

Keycloak kann ein komplettes Realm — inklusive Clients, Rollen, Groups, Authentication Flows, Themes-Referenzen und (optional) Users — in eine JSON-Datei exportieren. Diese Datei ist menschen­lesbar, eignet sich für Code-Review und kann in Git versioniert werden. Damit wird das Realm-Modell zu Konfigurations-Code, der in CI-Pipelines ausgerollt werden kann:

# Realm exportieren
kc.sh export \
  --dir /tmp/realms \
  --realm mitarbeiter \
  --users different_files

# Realm importieren (idempotent in den meisten Konstellationen)
kc.sh import \
  --file /tmp/realms/mitarbeiter-realm.json

In Kubernetes-Setups übernimmt die KeycloakRealmImport-CRD diesen Schritt deklarativ. Realm-JSONs landen in einem Git-Repository, Pull-Requests werden reviewt, ein CI-Job rollt die geänderte Konfiguration aus. IAM-Konfigurations­änderungen wie „Lehrling X erhält Zugriff auf Verfahren Y" werden damit zu auditierbaren Git-Commits — eine der wichtigsten Compliance-Eigenschaften überhaupt.

Backup-Strategie

Backup heißt für Keycloak drei Dinge gleichzeitig. Erstens: ein vollständiges PostgreSQL-Backup mit pg_dump oder pgBackRest — die DB ist die Quelle der Wahrheit für Realms, Users, Clients, Sessions sind hingegen nur transient. Zweitens: regelmäßiger Realm-Export der wichtigen Realms ins Git-Repository — als zweite, deklarative Sicherung, die auch eine versehentliche Realm-Löschung überlebt. Drittens: ein Backup der Theme-JARs und der keycloak.conf, idealerweise als Teil des Container-Image-Build-Prozesses, sodass das Image selbst reproduzierbar ist.

Zero-Downtime-Upgrades

Keycloak unterstützt Rolling Updates innerhalb derselben Major-Version unproblematisch — alte und neue Knoten arbeiten im selben Infinispan-Cluster zusammen. Der Pfad für ein Major-Upgrade (etwa von 25 auf 26) sieht so aus:

  1. Datenbank-Backup erstellen. Major-Upgrades führen Schema-Migrationen aus, die nicht rückwärtskompatibel sind.
  2. Release-Notes der dazwischenliegenden Versionen lesen — es gibt regelmäßig Deprecations und Migrations-Hinweise, die im laufenden Betrieb relevant sind.
  3. In einer Staging-Umgebung mit einer Kopie der produktiven Datenbank den Upgrade-Schritt durchspielen. Realms öffnen, Test-Login durchführen, Theme-Rendering prüfen.
  4. In Produktion einen Knoten nach dem anderen tauschen. Mit dem Keycloak Operator passiert das automatisch über die Pod-Update-Strategie.

In den meisten unserer Projekte planen wir Major-Upgrades zweimal im Jahr — Keycloak veröffentlicht jährlich vier Major-Versionen, aber n-2 Versionen werden unterstützt. Häufiger upgraden lohnt sich nur, wenn ein konkretes Feature aus einer neuen Version gebraucht wird.

Wann Keycloak passt — und wann eine SaaS-Lösung

Keycloak ist mächtig, etabliert und in regulierten Branchen verbreitet — aber kein Werkzeug für jeden Anwendungsfall. Eine ehrliche Empfehlung am Ende.

Passend, wenn …

  • der Betrieb self-hosted erforderlich ist — KRITIS, BAIT/VAIT, DORA, NIS2 oder vertragliche Anforderungen verbieten eine SaaS-Auslagerung der Identitäten;
  • OIDC und SAML nebeneinander gebraucht werden — moderne und Legacy-Anwendungen am selben IdP;
  • mehrere Realms oder Mandanten getrennt werden müssen — Mitarbeitende, Kunden, B2B-Partner in einer Plattform;
  • Custom Themes ein eigenes Branding und eine fachlich angepasste Benutzerführung verlangen;
  • eine vorhandene Identitätsquelle (Active Directory, OpenLDAP, BundID) per Federation oder Brokering eingebunden werden soll;
  • FAPI-konforme Banking-APIs oder andere hochregulierte Schnittstellen ausgegeben werden;
  • Realm-Konfiguration als Code im Git-Repository mit Pull-Request-Reviews gepflegt werden soll.

Weniger geeignet, wenn …

  • eine vollständig gemanagte SaaS-Lösung ohne Eigenbetrieb gewünscht ist — Auth0, Okta, Microsoft Entra External ID bieten dort deutlich weniger operative Tiefe;
  • das Setup ausschließlich auf Microsoft-Stack aufbaut und Entra ID ohnehin als Mandanten-IdP läuft — dann verdoppelt Keycloak die Komplexität;
  • das Volumen so klein ist (wenige hundert User, ein einziges Frontend), dass ein gemanagter Dienst wirtschaftlicher ist;
  • keine Engineering-Kapazität für Upgrade-Disziplin, Cluster-Betrieb und Theme-Pflege vorhanden ist — Keycloak verzeiht Vernachlässigung schlechter als ein SaaS-Werkzeug.

In den meisten regulierten Branchen, mit denen wir arbeiten, ist Keycloak die richtige Wahl — Energieversorger, Banken, Versicherungen, die öffentliche Verwaltung. Die Kombination aus Standardkonformität, Self-Hosting und Open-Source-Lizenz schlägt in dieser Zielgruppe praktisch jede SaaS-Alternative. Im nächsten Artikel dieser Reihe gehen wir auf das dritte Themenfeld ein, das wir in Kundengesprächen besonders häufig diskutieren: Voice-Agenten architektonisch — wie sich ein produktiver Sprachassistent für die kommunale Bürgertelefonie aufbaut, mit ElevenLabs, VAPI und der Anbindung an Anthropic und OpenAI.

Keycloak-Migration oder HA-Aufbau?

Wir prüfen gemeinsam mit Ihrem Team Ihre IAM-Landschaft — Realm-Design, Cluster-Topologie, Postgres-Sizing, Theme-Strategie, Migration vom WildFly- auf die Quarkus-Distribution, FAPI-Anforderungen, GitOps-fähige Realm-Konfiguration. Das Ergebnis: ein konkreter Maßnahmen­plan, abgestimmt auf Größe und Reife Ihrer Plattform.

Gespräch vereinbaren