Keycloak an die eigenen Anforderungen anpassen und erweitern

April 20, 2022

Tags: #keycloak #oauth2 #oidc #authentifizierung #identitymanagement

Nichts ist unterschiedlicher als die Anforderungen an die Authentifizierung.
Jedes Unternehmen hat hier andere Ansichten, Wünsche und Herausforderungen. Genauso, wie Unternehmen hier eigensinnig - oder opinionated sind, ist es Keycloak. Keycloak macht vieles gut, aber eben nicht alles so, wie man es möchte. Um allen gerecht zu werden, führt kein Weg an Custom Keycloak-Extensions vorbei.

Dieser Text erschien zuerst im Java Magazin 7.2022

Im vorangegangenen Artikel, haben wir bereits ausführlich diskutiert, warum Keycloak als zentrale Identity- und Access-Management- und Single-Sign-On-Lösung (im Unternehmen) eingesetzt werden sollte, und welche Features und Vorteile es hierfür mitbringt. (Falls Ihr den Artikel noch nicht gelesen habt, bitte noch mal zurückblättern.) Was aber, wenn die Standard-Features nicht ausreichen, oder es Anforderungen and die Authentifizierung gibt, die mit dem Default-Verhalten nicht erreicht werden können?

SPI Übersicht in der Keycloak Admin-Konsole Keycloak bringt zwar viel Funktionalität mit, macht bestimmte Dinge aber auf eine eigene Art und Weise, die vielleicht nicht für jeden Use-Case die Beste ist. Eben eigensinnig oder neudeutsch auch “opinionated” genannt. Um den Anwendern und Unternehmen, die Keycloak einsetzen aber dennoch ein mächtiges Werkzeug an die Hand zu geben, baut Keycloak auf das Standard Java SPI Pattern. Nahezu jedes Feature und jede Funktionalität ist in Keycloak über ein SPI implementiert. Nebenstehende Abbildung zeigt die Übersicht der zur Verfügung stehenden SPIs in der Keycloak Admin-Konsole.

Je nach Feature gibt es ein Default-SPI oder mehrere SPIs, die dann zur Laufzeit aufgrund von bestimmten Bedinungen gewählt werden. Der Kern von Keycloak arbeitet nur über die Interfaces und die jeweiligen Implementierungen bestimmen, welche Daten wie verarbeitet und überprüft werden. Damit wird Keycloak von einem simplen SSO-Produkt zu einer optimalen Plattform, um darauf aufbauend die eigene Logik und das gewünschte Verhalten zu implementieren und abzubilden.

Über diese SPIs können wir nun beliebige Implementierungen hinzufügen, die dann unser gewünschtes Verhalten abbilden. Dabei müssen wir - SPI Pattern sei Dank - keine eigene Keycloak Distribution kompilieren und erstellen. Wir können unsere SPI-Implementierungen einfach als JAR paketieren und zur Keycloak-Distribution deployen bzw. kopieren. Somit bleiben wir hinsichtlich des Kerns von Keycloak größtmöglich Upgrade-fähig, ohne dessen Code anfassen oder verändern zu müssen. Bei Versionsupgrades müssen wir lediglich unseren eigenen SPI-Code überprüfen und ggf. anpassen. Hierbei muss man hervorheben, dass die meisten Interfaces bislang eine sehr stabile Signatur haben und sich oft nur marginal geändert haben. Ausnahmen bestätigen auch hier die Regel und es kann keine Garantie für die Zukunft gegeben werden. Es gibt eine Aufteilung der SPIs nach öffentlichen und privaten Interfaces. Wobei auch die privaten Interfaces für die Anpassung und Implementierung eigener Logik verwendet werden dürfen (und müssen), es kann lediglich sein, dass die privaten Interfaces sich schneller und ohne Ankündigung ändern, das sollte man wissen und im Hinterkopf behalten. Es ist aber kein Verbot oder “no-go” die Interfaces zu nutzen! Der SPI-Mechanismus ist, auch wenn sich Interfaces ändern, deutlich einfacher zu handeln als eine Änderung des kompletten Keycloak-Codes an die eigenen Bedürfnisse.

Schauen wir uns nachfolgend ein paar der wichtigsten und interessantesten SPIs an und was man mit diesen machen kann:

Themeing

Die erste Anpassung, die Unternehmen am häufigsten vornehmen, ist, das Look’n’Feel der Loginseite und der Emails an das Unternehmens-Layout (oder auch Corporate Identity) anzupassen. Nur von der “Loginseite” zu sprechen, ist hier zwar zu kurz gedacht, da das Login-Theme, wie es richtig heißt, alle Seiten und Formulare umfasst, die zur Authentifizierung eines Benutzers benötigt werden, also auch sowas wie “Passwort vergessen”, “Passwort ändern”, “Einrichtung OTP”, etc.

Keycloak Loginseite mit Default- (Patternfly, links) und Custom-Layout (Bootstrap, rechts) Das Default-Templating basiert auf der Freemarker Template-Engine. Sicherlich nicht die neueste und modernste Templating-Engine, aber eine, die ihre Arbeit zuverlässig und gut verrichtet, da sie doch über die Jahre sehr gut gereift ist. Und eine “langweilige und alte” Technik muss ja nicht immer die Schlechteste sein. Das Standard-Layout, welches mit Keycloak ausgeliefert wird, basiert auf dem UI-Framework Patternfly, welches ebenfalls, wie Keycloak selbst, aus dem Hause Red Hat kommt. Patternfly selbst ist ein Bootstrap (v4) Derivat, ob nun “besser” oder “schlechter”, sei dahingestellt. Allerdings ermöglicht es damit, recht einfache Anpassungen ohne großen Aufwand vorzunehmen und fast nur die CSS-Ressourcen auszutauschen und CSS-Klassenvariablen anzupassen, um ein neues Look’n’Feel zu erhalten. Nebenstehende Abbildung zeigt ein Vergleich der Standard-Loginseite mit Patternfly-Design (links) und einem Bootstrap-Design (rechts).

Passt das generierte HTML nicht zum gewünschten Zieldesign, kann auch das über eine Anpassung der verschiedenen .ftl (Freemarker Template) Dateien verändert werden. Die Templates sind dabei so aufgebaut, dass es ein Template für den “Rahmen” und Basis-Layout der Seiten gibt und die verschiedenen fachlichen Inhalte über dedizierte einzelne Templates gerendert werden.

Gleich wie das Login-Theme, kann auch das “E-Mail-Theme” angepasst werden. Keycloak verschickt im Rahmen von Verifizierungen und Passwort-Vergessen Anforderungen (und anderen UseCases) diverse E-Mails, die allesamt ebenfalls gestylt werden können. Alle E-Mails werden als Text- und HTML-Part erzeugt, somit steht auch hier der volle Umfang des Customizings zur Verfügung.

Formulierungen und Übersetzungen von Labels können über Standard Java Message-Resource-Bundles an die eigenen Wünsche angepasst werden, müssen also nicht fest in den Templates hinterlegt werden. Im Default sind die Message-Resourcen systemweit für Keycloak gültig, können aber auch für einzelne Realms erweitert und überschrieben werden.

Das Templating in Keycloak geschieht über ein Vererbungsmechanismus, d.h. es kann (sollte!) von einem Basis-Theme geerbt werden, welches durch das eigene Theme erweitert wird. In dem eigenen Theme müssen dann nur jeweils die Ressourcen angepasst oder überschrieben werden, die verwendet und geändert werden sollen. Wird in der gesamten Vererbungshierarchie letztendlich immer irgendwo vom Keycloak-Base-Theme geerbt, stellt man damit auch sicher, dass Erweiterungen durch neue Keycloak-Versionen, auch in bestehenden eigenen Themes Berücksichtigung finden und es so zu keinem Laufzeitfehler führt. Ein neues Base-Template sieht dann ggf. nicht so schön aus, da es noch nicht angepasst wurde, lässt den Benutzer aber immerhin noch mit Keycloak arbeiten.

Wie die Templates gerendert werden und welche Beans und Daten während des Renderings zur Verfügung stehen, steuern die passenden ThemeProvider SPIs, genauer das Forms SPI und das EmailTemplate SPI. Sollen weitere Daten in den Templates zur Verfügung stehen, oder soll eine andere Logik oder gar eine andere Templating-Engine (z.B. Thymeleaf, Mustache, serverseitiges React, usw…) zur Anwendung kommen, kann dies über eine eigene Implementierung des entsprechenden SPIs geschehen.

Außer dem Login- und E-Mail-Theme gibt es (natürlich) auch noch die Möglichkeit, das Theme der Account-Anwendung und der Admin-UI anzupassen. Für beide Anwendungen existiert aber auch eine API, so dass es hierfür auch möglich wäre, komplett eigene Applikationen zu entwickeln und zu betreiben, in denen man dann noch mal ganz andere und erweiterte Möglichkeiten bekäme.

EventListeners

EventListener sind sicherlich das “HelloWorld”-Beispiel für Keycloak, wenn es um die Implementierung von eigene Erweiterungen geht, ist das EventListener SPI doch das einfachste Interface. Das SPI stellt mit dem EventListenerProvider gerade mal zwei fachliche Methoden bereit, die implementiert werden müssen: onEvent(Event event) für die “normalen” Login-Events und onEvent(AdminEvent event, boolean includeRepresentation) für die Events, die bei der Arbeit mit der Admin-API, also auch über die Admin-Webkonsole, gesendet werden.

Keycloak emittiert zwar für viele Vorgänge im Server Events, stellt selbst aber keine großen Möglichkeiten der Verwaltung der Events zur Verfügung. Lediglich ein Speichern der Events in der eigenen Datenbank und das Schreiben der Events ins Log des Servers stehen zur Verfügung. Auswerten kann man hiermit diese Events so gut wie gar nicht. Ein EventListener eignet sich damit hervorragend, um die Keycloak-Events z.B an ein externes System weiterzuleiten. Das könnte eine Datensenke wie z.B. ElasticSearch oder Prometheus sein, um Eventdaten komfortabler auswerten und aggregieren zu können, oder auch ein externer Eventbus/Topic, auf den sich andere Systeme registrieren können, um Keycloak-Events zu verarbeiten.

EventListener können aber auch zur internen Verarbeitung genutzt werden. So kann z.B. auf ein Login-Event reagiert werden und der letzte Login-Zeitstempel im Userprofil gespeichert werden, wenn dieser an einer anderen Stelle, z.B. in einem Token-Mapper, benötigt wird. Wenn im Fehlerfalle bestimmter Events Attribute am User geschrieben werden sollen, ist auch das möglich, denn zu jedem Erfolgsevent gibt es auch immer ein Fehlerevent. Einige Unternehmen wollen, dass es für einen User z.B. nur eine aktive Session gibt. Keycloak erlaubt im Standard mehrere Sessions mit unterschiedlichen Geräten. Mit einem EventListener kann beim Auftreten eines Login-Events geprüft werden, ob ein Benutzer bereits andere Sessions hat; diese alten Sessions können dann beendet und entfernt werden. (Ein Beispiel hierfür findet sich im Keycloak Extensions Demo Repository)

Welcher UseCase auch immer zu Grunde liegt, mit Events können viele davon abgedeckt werden. Allerdings ist es nicht möglich, direkt auf den Authentifizierungsvorgang Einfluss zu nehmen, hierfür sind die im nächsten Abschnitt beschriebenen Authentifikatoren verantwortlich.

Authenticators

Das Authenticator SPI ist eines der zentralsten Interfaces in Keycloak. Hierüber kann der Authentifizierungsvorgang von Benutzern und Clients gesteuert werden. Im Standard bringt Keycloak bereits eine ganze Menge von Authentifikatoren mit, wie z.B. das Standard Benutzername/Passwort-Formular, zwei getrennte Formulare für die Eingabe von Benutzername und Passwort, die Eingabe eines 2. Faktors über TOTP, die Erkennung eines bereits authentifizierten Benutzers über Cookies, die Authentifizierung von Clients anhand von Client-ID und Secret oder JWTs, und vieles, vieles mehr.

Möchte man z.B. für eine 2-Faktor Authentifizierung einen anderen Mechanismus als die in Keycloak enthaltene OTP-Möglichkeit nutzen, muss man diese als eigene SPI Implementierung auf Basis des Authenticator Interfaces zur Verfügung stellen. So kann z.B. eine 2-Faktor Authentifizierung über einen per SMS versendeten Code implementiert werden. Ein Beispiel hierfür ist im Repository dasniko/keycloak-2fa-sms-authenticator zu finden. Natürlich ist aber auch die Anbindung anderer Systeme möglich.

Ein weiteres Beispiel für einen Authentifikator ist der Versand eines sog. “Magic Links” an den sich gerade anmeldenden Benutzer (Beispiel siehe im Demo-Repository). Dieser Link kann dann an Stelle eines Passwortes genutzt werden, damit sich Benutzer authentifizieren können. Die “Sicherheit” wird damit dann vom Wissen des Benutzers in die Mailbox des Benutzers verlagert (mitsamt aller Vor- und Nachteile). Der Link sollte dann nur einmalig und nur eine sehr kurze Zeit gültig sein.

Custom Authenticationflow mit bedingter 2-Faktor SMS Authentifizierung Mehrere Authentifikatoren können zu einem logischen Ablauf, auch mit Bedingungen, in einem Authenticationflow zusammengefasst werden. Ein User oder Client ist erst dann vollständig authentifiziert, wenn der jeweilige Flow erfolgreich verarbeitet wurde. Dabei ist es nicht immer notwendig, dass alle Schritte in einem Flow durchlaufen werden. Je nach Art des Schrittes (benötigt, optional, bedingt, deaktiviert) kann ein Flow auch schon nach einem einzigen erfolgreichen Schritt beendet werden. Die bedingte Ausführung eines Schrittes kann z.B. die Evaluierung von Rollen sein, so dass Benutzer mit bestimmten Rollen noch einen zweiten Faktor ausführen müssen, andere aber nicht. Nebenstehende Abbildung zeigt einen eigenen Authenticationflow mit bedingter 2-Faktor SMS-Authentifizierung, wenn ein Benutzer die admin-Rolle hat.

Möchte man Daten zwischen verschiedenen Authentifikatoren, also Schritten im Flow, austauschen, so kann man das über “Notes” in der zur Verfügung stehenden AuthenticationSession erreichen. Diese AuthenticationSession lebt während des gesamten Authentifizierungsvorganges. Es ist ebenfalls möglich, Daten aus der Authentifizierungssession mit in die im Anschluss erzeugte Benutzersession zu übernehmen (Clients bekommen i.d.R. keine Session).

Required Actions

Keycloak bietet die Möglichkeit an, dass Benutzer, bevor sie endgültig authentifiziert sind, bestimmte Aktionen durchführen und erfüllen müssen. Das kann vom einfach bestätigen der E-Mail-Adresse (durch Klick auf den versendeten Link), Aktualisieren des Benutzerprofils, Akzeptieren von (Geschäfts-)Bedingungen und Änderung des Passworts verschiedene Aktionen sein.

Hat ein Unternehmen eigene Anforderungen an durchzuführende Aktionen, können diese mittels der Implementierung des RequiredActionProvider Interfaces zur Verfügung gestellt werden. RequiredActions können auch mit Authentifikatoren kombiniert werden.

Nehmen wir den im vorherigen Abschnitt angesprochenen 2-Faktor SMS-Authentifikator: Für den Versand der SMS ist die Angabe (und ggf. Bestätigung) einer Mobilfunknummer des Benutzers notwendig. Der Authentifikator kümmert sich aber nur darum, dass die SMS versendet und der dann eingegebene Code überprüft wird. Stehen die dafür notwendigen Daten, hier die Mobilfunknummer, nicht zur Verfügung, kann der Authentifikator eine RequiredAction aufrufen die den Benutzer zur Eingabe einer entsprechenden Nummer auffordert. Bei Bedarf und Implementierung muss der Benutzer diese Nummer zusätzlich noch verifizieren, bevor die Konfiguration der Mobilnummer erfolgreich abgeschlossen ist. Eine einfache Variante einer solchen RequiredAction ist im Demo-Repository enthalten.

Ist eine RequiredAction einmal einem Benutzer zugewiesen, muss dieser die Aktion zuerst erfolgreich ausführen, bevor der Benutzer authentifiziert ist und die Action wieder vom Benutzer entfernt wird. RequiredActions werden immer am Ende eines Authentifizierungsflows ausgeführt, es sei denn, ein Authentifizierungsschritt entscheidet aufgrund fehlender Daten, dass die Aktion vorzeitig aufgerufen werden soll.

Zusätzlich zum kontextbezogenen Aufruf einer Aktion aus einem Authentifikator, kann eine RequiredAction auch von einem Administrator einem Benutzer manuell zugewiesen werden, es kann programmatisch aufgrund von verschiedenen Bedingungen die Ausführung “erzwungen” werden und es besteht die Möglichkeit, eine RequiredAction manuell per URL-Aufruf auszuführen.

User Storage

Neben der Default-Speicherung der Userdaten in der Keycloak-eigenen Datenbank und der Konfiguration eines externen Verzeichnisses über das LDAP-Protokoll, erlaubt Keycloak über das User Storage SPI auch die Anbindung und Integration von beliebigen Verzeichnissen oder Systemen als Datenquelle (und -ziel!) von Benutzerdaten. Die im Standard zur Verfügung stehende LDAP-Konfiguration ist bereits eine (sehr umfangreiche) Implementierung des User Storage SPIs. Eigene Implementierungen können mit ein wenig Aufwand selbst hinzugefügt werden.

UseCases für die in Keycloak User Federation genannte Funktion können sein, dass es ggf. bereits bestehende Datenquellen, evtl. aus einem Alt- oder Fremdsystem, gibt, die weiter genutzt werden sollen. Gerade wenn Passwörter weiter gelten sollen, kommt eine direkte Benutzermigration meist nicht in Betracht, da das neue System das genutzte Hashingverfahren nicht unterstützt. Keycloak könnte hier zwar ebenfalls über ein SPI den gewünschten Hashing-Algorithmus implementieren, aber auch über eine User Federation mit der bestehenden Datenquelle lassen sich Altdaten nutzen.

Alternativ könnte ein Grund für einen eigenen User Storage sein, dass ein komplexeres Datenmodel rund um die Benutzer gepflegt wird, bzw. existiert. Keycloak selbst bietet nur ein sehr simples Key-Value (Map) Datenmodell an, über das erweiterte Hierarchien und Abhängigkeiten nur sehr schwer abbildbar sind. So kann mit der Implementierung eines User Storage SPIs jedes externe System angebunden und die Daten entsprechend für das Keycloak Datenmodell angepasst werden, ohne dass Änderungen am Quellsystem oder Keycloak selbst notwendig werden.

Auf welche Art auf das externe Verzeichnis zugegriffen wird, ist dabei egal. Das System muss hierbei nur entsprechende programmatische Schnittstellen zur Verfügung stellen. Sei es eine (REST-)API oder auch der Zugriff direkt auf die Datenbank-Tabellen aus Keycloak heraus. (Fast) Alles, was per Java möglich ist, kann hier genutzt werden.

Das User Storage SPI stellt, anders als die meisten anderen SPIs, nicht nur ein Interface zur Verfügung, sondern je nach Funktionsumfang, mehrere unterschiedliche Interfaces, die, je nach Wunsch und Anforderung, miteinander kombiniert werden können oder müssen.

Der nachfolgende Artikel von Sven-Torben Janus zeigt im Detail anhand eines Beispiel-Falles, wie eine Implementierung des User Storage SPIs aussehen kann. Außerdem ist auch im Demo-Repository eine Beispiel-Implementierung zu finden.

REST Ressourcen

Keycloak stellt für alle administrativen Dinge die Admin-REST-API bereit. Diese API wird u.a. auch von dem Admin-UI, dem Java-Admin-Client und des Shell-Tools kcadm.sh verwendet. Außerdem stehen alle, die für die Authentifizierung benötigten OIDC- und SAML-Endpunkte zur Verfügung.

Möchte man nun eigene Endpunkte zu Keycloak hinzufügen, um aus einem Client heraus z.B. spezifische ActionTokens zu erzeugen und abzurufen oder mit nur einem Request mehrere Admin-API Calls zusammenzufassen, so kann man aus dem Resource SPI das Interface RealmResourceProvider implementieren und so die gewünschten Endpunkte anbieten.

Zu beachten sind zwei Dinge:

  1. Die mit einem deployten ResourceProvider zur Verfügung gestellten Endpunkten stehen in jedem(!) Realm zur Verfügung. Die Endpunkte werden dabei nach dem Muster /realms/{realmName}/{resourceProviderId}/{paths...} veröffentlicht. Soll ein ResourceProvider nur für einen einzigen oder eine Teilmenge der Realms zuständig sein, so muss dies programmatisch im Code behandelt werden.
  2. Alle Endpunkte sind offen und ungeschützt, d.h. ohne vorherige Authentifizierung aufrufbar. Jede Implementierung muss selbst dafür Sorge tragen, dass, wenn gefordert, nur authentifizierte und autorisierte Anfragen verarbeitet werden. Leider stehen hierfür keine einfach und deklarativ zu nutzenden Annotation o.ä. bereit. Diese Aufgabe muss programmatisch durchgeführt werden. Keycloak stellt hierfür immerhin ein paar Hilfsklassen und -methoden zur Verfügung, damit nicht alles selbst implementiert werden muss und bestimmte repetitive Dinge, wie z.B. das Auslesen der Token-Infos aus dem Request oder aus einem Cookie, usw. vereinfacht werden.

Ein einfaches Beispiel für eine Realm-Ressource mit öffentlichem und abgesichertem Endpunkt ist im Demo-Repositoryfootnote:repo[] zu finden.

Testen von Keycloak-Extensions

Zu jeder Implementierung gehören auch Tests. Die Aussagekraft von Unit-Tests bei Keycloak-Extensions kann durchaus in Frage gestellt werden, da sehr viel vom “drumherum” gemockt werden müsste, um die SPI-Implementierungen zu testen. Viel mehr möchte man doch wissen, wie sich die Extension in einem “richtigen” Keycloak Server verhält.

Zu diesem Zweck gibt es die Testcontainers-Erweiterung testcontainers-keycloak, mit der sich die meisten Extensions sehr komfortabel in einer echten Keycloak-Umgebung testen lassen. Abhängigkeiten zu bestehenden Deployments und Installationen, Beeinträchtigungen der Tests durch andere Entwickler oder Beeinträchtigungen anderer Entwickler durch die Extension-Tests können somit sehr einfach vermieden werden und man erhält aussagekräftige Ergebnisse aus den eigenen Integrationstests.

Einige der im Demo-Repository enthaltenen Extensions verfügen ebenfalls über Tests mit dem Keycloak-Testcontainer.

Was noch?

Die in diesem Artikel beschriebenen SPIs und Erweiterungen stellen natürlich nur einen kleinen Teil der in Keycloak vorhandenen Extensionpoints dar. So gibt es zudem noch weitere SPIs, wie z.B. für:

Einen guten Einstiegspunkt in die Welt der Keycloak-Extension-Entwicklung, bietet wie immer auch die in weiten Teilen sehr gute Keycloak-Dokumentation, hier speziell der Keycloak Server Developer Guide.

Eigene Extensions machen Keycloak zu einem noch mächtigeren Werkzeug für das Identity- und Usermanagement und für den Single-Sign-On Einsatz im Unternehmen, bzw. für den jeweiligen Anwendungszweck. Einmal damit gestartet und sich etwas eingearbeitet, stellen Keycloak-Erweiterungen keine große Hürde dar, weder die initiale Implementierung noch die Wartung und Pflege derer. Natürlich geht man mit jeder Extension auch eine Verpflichtung für diese ein, das ist aber hier nicht anders als mit allen anderen Software-Komponenten. Man hat aber “nur” die Verpflichtung der Erweiterung gegenüber, nicht über einen angepassten und abgeleiteten Keycloak-Kern, der eine deutlich größere Aufgabe und teilweise auch Hürde darstellen würde.

Aus meiner Erfahrung heraus kann ich jedes Unternehmen und jeden Entwickler nur ermutigen, den Weg des Keycloak-Customizings zu gehen und das Tool den Benutzenden und Prozessen anzupassen und nicht umgekehrt.

Hier biete ich ein Einstiegstraing in die Keycloak-Extension-Entwicklung an.

Du bist auf der Suche nach Keycloak Beratung, Unterstützung, Workshops oder Trainings?

Nimm Kontakt mit mir auf!

« Keycloak als OpenID Connect konforme IAM und SSO Lösung KEYCLOAK REALMS - What they acutally are and how to use them for Multi-Tenancy »