Keycloak als OpenID Connect konforme IAM und SSO Lösung
April 18, 2022
Tags: #keycloak #oauth2 #oidc #authentifizierung #identitymanagement
Das, was sich in den letzten Jahren im Bereich der allgemeinen Applikationsentwicklung durchgesetzt hat - nämlich das Vertrauen auf und Nutzen von Bibliotheken, Frameworks und Servern, die uns Entwicklern das Leben leichter machen und wir uns nicht mehr mit Request-/Response-Behandlung im Detail oder der Implementierung von Servlets wieder und wieder auseinandersetzen müssen, scheint nun auch im Bereich der Security, vornehmlich der Authentifizierung, Einzug zu halten. Ein begrüßenswerter Trend.
Dieser Text erschien zuerst im Java Magazin 7.2022
Viel zu lange herrschte leider die Meinung “Ach, diese eine Tabelle mit den Usernames und Passwörtern können wir doch auch selbst pflegen.” Dass daraus sehr schnell sehr viel mehr wird, die Anforderungen daran sehr hochgesteckt sind und die Implementierung und Pflege im Gesamtprojekt mehr Zeit und Aufwand kostet, als ursprünglich gedacht, kennen wir alle. Auch die Nutzung von Security-Frameworks und -Bibliotheken nimmt uns hier nur einen Teil davon ab. Ganz zu schweigen von potenziellen Sicherheitslöchern, die wir unbemerkt schaffen, da wir eher nicht die Securityexperten sind, die es an dieser Stelle bedarf.
Mit der wachsenden Zahl an Anwendungen, Mobilen Apps und verteilten APIs, ist das Handling von Benutzer- und Client-Identitäten und deren erfolgreiche Authentifizierung ebenfalls nicht einfacher geworden. Die schon seit ca. 20 Jahren existierenden SAML Spezifikationen definieren hier zwar schon recht gute Standards, sind aber nicht zuletzt aufgrund ihrer Historie eher umständlich zu handeln.
OAuth2 und OIDC
Um die Anwendung zu vereinfachen und auf die geänderten Technologischen Gegebenheiten anzupassen, wurde vor 10 Jahren die OAuth2-Spezifikation verabschiedet. OAuth2 dreht sich aber weniger um die Authentifizierung eines Benutzers und dessen Identität, genau gesagt lässt OAuth2 diese Daten sogar außen vor. Die Spezifikation behandelt die Autorisierung, die ein Benutzer (Resource Owner) einer Drittpartei (Client, vornehmlich eine serverseitige Anwendung) gibt, um auf Daten (Resources) in einem (Quell-)System (Resource Server) zuzugreifen. Hierfür muss sich der Benutzer noch im Gültigkeitsbereich des Quellsystems authentifizieren, um den Zugriff zu autorisieren. Diese Authentifizierung nimmt der Benutzer am Authorization Server vor, welcher die Identitätsdaten aber (nicht unbedingt) nach außen gibt (und wenn, dann nicht standardisiert). Der Authorization Server stellt dem Resource Owner dann ein Access Token aus (welches die Berechtigung auf die freigegebenen Daten enthält), welches sich der Client mit einer ClientID-/-Secret Kombination dann am Authorization Server abholen und mittels diesem auf die Daten am Resource Server zugreifen kann.
Das mag auf den ersten Augenblick etwas verwirrend wirken, weil neu. Zudem kommen viele neue Begriffe ins Spiel. Letztendlich ist die OAuth2-Spezifikation und die Grants (Abläufe, wie sich wer am AS authentifiziert und Zugriffe autorisiert), welche OAuth2 definiert, aber eine recht verständliche Definition. Leider definiert sie aber nicht, wie ein Client, also eine Drittpartei, den Benutzer selbst identifizieren kann. Das war zwar auch gar nicht gewollt, als man die Spezifikation erstellte, man wollte ja schließlich nur eine Spec zur Daten-Autorisierung durch den Daten-Eigner zwischen Applikationen erstellen, dennoch wollen oder benötigen Clients auch Informationen über die Identität des Benutzers selbst. Die meisten OAuth2-Server liefern zwar auch (einige wenige) Daten über den Benutzer mit, leider kann man sich darauf aber nicht grundsätzlich verlassen und auf das Format schon mal gar nicht. Zwei unterschiedliche OAuth2-Server können zwei völlig unterschiedliche Informationen über den Benutzer liefern.
An dieser Stelle setzt nun OpenID-Connect (kurz: OIDC) an (nicht zu verwechseln mit “OpenID”, Erklärung siehe Link). OIDC bringt wohl definierte und standardisierte Mittel und Endpunkte ins Spiel, mit denen die Identitäten von Benutzern einheitlich und verlässlich kommuniziert und konsumiert werden können. Es ist quasi so etwas wie ein einheitliches Namensschild. Dabei setzt OIDC technisch komplett auf den o.g. OAuth-_Grants_ auf und bringt im Wesentlichen u.a. folgende weiteren Standards mit:
ID Token und JWT
Zusätzlich zum Access Token, welches ja den Zugriff auf Daten autorisiert, daher der Name access, transportiert das ID Token oder Identity Token Daten über die Identität des Benutzers. Dabei definiert OIDC zum einen das Format des Tokens selbst, welches dem JSON-Web-Token Standard entsprechen muss. In OAuth2 kann das Access Token auch ein sog. opaque Token sein, also ein nichtssagender String, der zunächst, bevor er verwendet werden kann, bzw. dass der Client weiß, mit was das Token verknüpft ist, am Authorization Server überprüft werden muss. Ein JWT ist aufgrund seiner Natur, der Signatur, die gültig sein muss und dem Ablaufzeitpunkt, nach dem das Token nicht mehr verwendet werden darf, ein self-contained Token und kann auch ohne die Überprüfung am Server gelesen und verwendet werden.
Neben dem Format, definiert OIDC aber auch die Inhalte des ID-Tokens.
Genauer gesagt, welche Claims (Attribute) im ID-Token für welche Informationen verwendet werden müssen.
So kann ein Konsument sicher sein, dass z.B. der Vorname eines Benutzers im Claim givenName
übermittelt wird, und nicht als firstName
oder über ein anderweitiges Attribut.
Dadurch dass das Token aber nicht zwingend verschlüsselt ist und damit von jedermann/-frau/-system eingesehen und gelesen werden kann, sollten darüber (natürlich) keine sensiblen Informationen transportiert werden.
Sobald ein Server ein ID-Token ausstellt und ausliefert, damit also die OIDC-Spec befolgt, ist der Server im Sprachgebrauch kein Authorization Server mehr, sondern ein Identity Provider.
User-Info Endpunkt
Da ein Token nur eine gewisse Menge an Daten aufnehmen kann oder soll, können erweiterte Profildaten eines Benutzers über den sog. UserInfo-Endpunkt von den Clients abgeholt werden. Für diese Abfrage nutzen die Clients das Access Token des Benutzers, der Identity Provider kann dann anhand des Access Tokens den entsprechenden Benutzer identifizieren und liefert die Profildaten zurück.
.well-known Endpunkt
Damit OIDC Clients nicht umfangreich konfiguriert werden müssen, bietet die OIDC-Spec einen definierten Endpunkt an, über den sich Clients die komplette benötigte Konfiguration abholen können.
Unter dem Pfad /.well-known/openid-configuration
, der an die Basis-URL des Identity-Providers angehängt wird, finden sich alle URLs und Settings, die der Identity-Provider zur Verfügung stellt und die für eine OIDC-konforme Kommunikation notwendig sind.
So sind hierüber die URLs für die Authentifizierung zu finden, wie auch die URL, über die die Tokens bezogen werden können.
Um die Tokens zu überprüfen, wird hierüber auch der benötigte öffentliche Schlüssel angeboten.
Keycloak
Das mache ich mal eben schnell selbst… Die berühmten letzten Worte eines Softwareentwicklers, der die eigentlich gar nicht so komplexe Spezifikation selbst implementieren wollte, aber aufgrund der Detailtiefe nie mehr im Projekt und in Teammeetings gesehen wurde. Das Team setzte in der Zwischenzeit stattdessen auf Keycloak.
Und damit tat das Team gut daran. Auch wenn die Spezifikationen auf den ersten Blick nicht kompliziert wirken, stellt die Implementierung eines OpenID-konformen Identity-Providers, mit allen Teil-Spezifikationen, doch einen erheblichen Aufwand mit einer nicht zu unterschätzenden Komplexität dar. Einfacher ist es daher, auf Produkte und Projekte, wie eben Keycloak zurückzugreifen und sich den Features zu erfreuen, die Keycloak bereits mitbringt, ohne dass man auch nur eine Zeile Code schreiben muss.
Die Verwendung und Integration in Client-Applikationen ist denkbar einfach, stellt Keycloak doch einen OIDC-zertifizierten Identity-Provider dar, auf den man mittels beliebigen OIDC-Bibliotheken und -Adaptern zugreifen kann. Und diese stehen mittlerweile für so ziemlich alle Umgebungen, Programmiersprachen und Plattformen zur Verfügung.
Out-of-the-box ist dann ein Single-Sign-On zwischen allen Clients möglich, die im gleichen Keycloak-Gültigkeitsraum - einem Realm - konfiguriert sind. Das Konzept eines Realms kennt die Spezifikation nicht, ein Realm ist etwas Keycloak-spezifisches. Im Sinne der Spec stellt ein Realm einen eigenständigen Identity-Provider dar. Keycloak kann somit innerhalb eines Servers mehrere unabhängige Identity-Provider anbieten, die aber alle über einen zentralen, speziellen Realm - dem Master Realm verwaltet und administriert werden können. Der Master-Realm ist nicht für den operativen Einsatz von Clients und Benutzern gedacht, sondern ausschließlich für administrative Zwecke vorbehalten.
Schauen wir auf weitere Features, die Keycloak außer dem “normalen Login” noch mitbringt:
Passwort-Vergessen und zurücksetzen
Keycloak stellt Funktionen bereit, über die ein Benutzer selbständig einen Passwort-Vergessen oder -Zurücksetzen Prozess starten kann. Keycloak versendet daraufhin dem Benutzer eine E-Mail mit einem (begrenzt gültigen) Link, über den der Benutzer sich dann ein neues Passwort vergeben kann. Es wird nie ein Passwort von Keycloak selbst generiert und als temporäres Passwort versendet!
Passwort-Policies
Die Komplexität bzw. die Anforderungen an Passwörter können über viele unterschiedliche Regeln definiert werden. So können zum einen die gängigen Regeln wie Länge, Groß-/Kleinschreibung, Zahlen, Sonderzeichen, etc. definiert werden, aber auch erweiterte Regeln wie Ablaufzeitraum, keine der letzten n Passwörter, Hashing-Algorithmus und -Iterationen, etc.
OTP als 2. Faktor
An einer 2-Faktor-Authentifizierung gehen heute hoffentlich so gut wie keine Wege mehr vorbei. Keycloak bietet dafür im Standard ein OTP-Verfahren an, das entweder zeitbasiert (TOTP) oder zählerbasiert (HOTP) arbeitet und mit Apps wie z.B. Google Authenticator, Microsoft Authenticator, Authy, etc. konfiguriert und verwendet werden kann.
Account-Verifikation
Wenn gewünscht, muss ein Benutzer sein Konto über seine E-Mail-Adresse zunächst verifizieren, bevor er sich erfolgreich authentifizieren kann. Hierfür versendet Keycloak eine E-Mail an den Benutzer, ähnlich wie beim o.a. Passwort-Rücksetzungsprozess, mit einem begrenzt gültigen Link, den der Benutzer aufrufen muss, damit sein Konto als verifiziert gilt.
Account-Self-Service
Für die Basis-Account-Pflege bietet Keycloak eine eigene Account Console an, über die ein Benutzer, einmal angemeldet, seine eigenen Daten wie z.B. Vor- und Nachname und E-Mail-Adresse ändern kann. Über die Account Console kann außerdem die Neuvergabe eines Passwortes angestoßen werden, wie auch die Konfiguration von OTP als 2. Faktor. In einer einer weiteren View sieht der Benutzer, über welche Endgeräte er Keycloak-Sessions hält und für welche Clients diesem Benutzer Tokens ausgestellt wurden. Die Account Console kann im Aussehen und vom Funktionsumfang an die eigenen Wünsche angepasst werden.
Identity Brokering und Social Logins
Soll mit externen Identity-Providern oder Social-Logins zusammengearbeitet und den von diesen Parteien zur Verfügung gestellten Identitäten vertraut werden, kann ein Brokering mit diesen einfach konfiguriert werden. Die Überprüfung der Identität/die Authentifizierung wird dann von dem externen IdP vorgenommen, Keycloak vertraut dann diesen Parteien. Als Social-Logins stehen alle möglichen Plattformen wie z.B. Google, Twitter, GitHub, Facebook, LinkedIn, Microsoft, etc. zur Verfügung. Möchte man einen externen IdP über SAML oder OIDC-Protokoll integrieren (z.B. Azure Active-Directory), kann man hierfür die generischen Adapter konfigurieren.
User Federation
Existieren im Unternehmen (oder in der Applikations-Umgebung) bereits (Benutzer-)Verzeichnisse, wie z.B. ein LDAP oder ein Kerberos-Server, können diese rein konfigurativ als Datenquelle für die Benutzerauthentifizierung in Keycloak integriert werden. Keycloak arbeitet hier transparent mit den Daten, egal welchen Ursprungs sie sind. Passwörter werden immer gegen das externe Verzeichnis abgeglichen, Keycloak speichert hier keine Daten!
Verfügt ein Verzeichnis nicht über eine LDAP-Schnittstelle, kann per Erweiterung programmatisch eine entsprechende Verbindung implementiert und bereitgestellt werden. Siehe hierzu auch den nachfolgenden Artikel in diesem Heft.
Clustering
Ein Authentifizierungssystem gilt als Single-Point-of-Failure. Ist die Authentifizierung nicht verfügbar, kann kein Benutzer mit irgendeinem System arbeiten. Auch Systeme untereinander können nicht mehr arbeiten, da ein verantwortungsvolles Design auch eine Authentifizierung bei der Inter-Service-Kommunikation vorsieht. Aus diesem Grund ist das Authentifizierungssystem unbedingt hochverfügbar und so ausfallsicher wie möglich zu betreiben.
Keycloak kommt hierfür mit einem eingebetteten Infinispan-Server, der als verteilter Cache für die Versorgung der am Cluster beteiligten Keycloak-Knoten mit den entsprechenden Arbeitsdaten - vornehmlich den Benutzersessions - verantwortlich ist. Der initiale Konfigurationsaufwand ist überschaubar und ein einfacher Cluster binnen wenigen Minuten aufgesetzt und betriebsbereit. Soll der Cluster größer werden, hat man alle von Infinispan zur Verfügung gestellten Optionen zur Hand, um dies anzupassen. Auch ein externer, von Keycloak losgelöster Infinispan-Cluster kann hierfür verwendet werden.
Anpassungen & Erweiterungen
Die obigen Features können hier nur eine Auswahl aus dem gesamten Umfang von Keycloak darstellen. Keycloaks Feature-Implementierung basiert auf dem Java Service Provider Interface (SPI) Pattern und kann umfangreich an die eigenen Bedürfnisse und Anforderungen angepasst und um neue Funktionen erweitert werden. Custom Themes, und damit eine Anpassung des Look’n’Feels der Login-Seiten, ist dabei nur die einfachste und geringste Möglichkeit. Welche umfangreichen Möglichkeiten außerdem zur Verfügung stehen, zeigt der nachfolgende Artikel “Keycloak an die eigenen Anforderungen anpassen” in diesem Heft.
Hosting und Datenhoheit
Keycloak ist naturgemäß nicht der einzige Authentifizierungs-Player am Markt. Neben Keycloak sind auch Auth0 und Okta sicherlich die beiden bekanntesten Plattformen, die Authentifizierung als Service anbieten. Aber genau hier liegt einer der großen und entscheidenden Unterschiede: Keycloak ist als eines der ganz wenigen Systeme kein gemanagter Service, den man einkauft. Keycloak ist als eines der ganz wenigen Systeme, die selbst betrieben werden können - oder müssen, je nach Blickwinkel. Ein eigener Betrieb und ein eigenes Hosting bieten aber den unschlagbaren Vorteil, dass auch die Daten und der Zugriff auf diese in der eigenen Hoheit und Zuständigkeit bleiben. Wer möchte schon gerne die mit am schützenswertesten Daten - Benutzerdaten inkl. Passwörter - einem fremden Anbieter überlassen? Und dann ggf. noch Nicht-EU Firmen, was im Kontext der DSGVO als höchstproblematisch anzusehen sein kann.
Dann doch lieber den Aufwand eingehen, und das System selbst betreiben. Ja, es ist ein zusätzliches System, das betreut werden muss, inkl. Updates, Bugfixes, Betrieb, Monitoring, und allem was dazu gehört. Und einen zuverlässigen und hochverfügbaren Identity-Provider zu betreiben, ist eine Herausforderung, keine Frage. Aber die Sicherheit der Benutzerdaten und damit auch das Ansehen des Betreibers - unseres eigenen Unternehmens - sollte uns das wert sein. Ja, auch hier kann man, trotz vielen fertig zu nutzenden Features, immer noch viel falsch machen. Gerade bei einigen Konfigurationen gibt es bestimmte Dinge zu beachten, dass das System nicht komplett offen für Angriffe ist. Aber genau das richtig zu konfigurieren, ohne noch zusätzlich für die korrekte Implementierung verantwortlich zu sein, was bei einer Eigenlösung der Fall wäre, schafft schon sehr viel Raum für ein sicheres Systemumfeld mit einer zuverlässigen Authentifizierung.
Keycloak.X - die Zukunft gehört Quarkus
Keycloak basierte seit 2013, als es das Licht der Welt erblickte und damit bereits frühzeitig eine OIDC-Implementierung bereitstellte, auf dem Wildfly-Applicationserver.
Im Grunde war Keycloak “nur” eine Webapplikation und bedingte den Einsatz eines App-Servers.
Ganz frühe Ansätze, Keycloak als Plattform-unabhängige .war
Distribution zur Verfügung zu stellen, wurden schnell fallen gelassen, da es verschiedene Probleme mit sich brachte.
Da Wildfly auch schon immer einen HA (HighAvailability = Hochverfügbarkeit) Modus mit einem eingebetteten Infinispan mitbrachte, konnte das HA-Cluster-Feature recht einfach genutzt und in Keycloak integriert werden.
Der Einsatz eines App-Servers ist an sich nicht verwerflich, hat jedoch auch so seine Nachteile. Gerade in aktuellen Projekten mit Container-basierten Umgebungen, sind App-Server umständlicher zu verwalten als für diese Umgebungen optimierte Anwendungen. Gleichzeitig arbeitet ein App-Server auf einer bestimmten Version der Jakarta-EE Spezifikation und ist damit nicht immer auf der Höhe der Zeit der im Java-Ökosystem angebotenen Möglichkeiten (was keine Kritik an der EE-Spec sein soll, ganz im Gegenteil, in bestimmten Bereichen ist es gut, solche eine Spec zu haben).
Mit Quarkus veröffentlichte Red Hat im Jahre 2019 ein Application-Framework, welches die Entwicklung von für Container-basierte Umgebungen optimierte Anwendungen und Services unterstützt (Details können unter dem angegebenen Link nachgelesen werden).
Da Keycloak zunehmend auch in containerisierten Umgebungen Anwendung findet, wurde seit 2020 konzentriert an einer Migration vom Wildfly-Appserver zum Quarkus Application-Framework gearbeitet. Diese Migration stellt nur einen, aber den benötigten Anfangspunkt für viele weitere Modernisierungen in Keycloak dar. Unabhängiger von einer konkreten Appserver Version zu sein, heißt flexibler neue Java-Möglichkeiten ausschöpfen zu können und so die Anwendung für die Zukunft besser optimieren zu können. Startupzeiten und geringerer Speicherverbrauch sind dabei nur zwei von vielen weiteren Vorteilen. Die Vorteile für eine containerisierte Umgebung nutzen zu können heißt aber nicht gleichzeitig, dass Keycloak nur noch per Container betrieben werden kann. Nach wie vor kann Keycloak auch als klassisches Deployment ohne Container ausgeführt werden!
Mit Keycloak in Version 17 ist im Februar 2022 die erste Quarkus-basierte Keycloak Distribution erschienen. Wurde bis zu diesem Termin bei den Previews immer von Keycloak.X gesprochen, findet der Name eher nur noch intern Verwendung. Keycloak heißt weiterhin Keycloak, meint aber die Quarkus-Distribution. Wer ab Version 17 noch die Wildfly-Distribution einsetzt, verwendet Keycloak Legacy. Die Wildfly-basierte Keycloak-Distribution soll allerdings nur noch bis September 2022 unterstützt werden. Unternehmen, die noch mit der Wildfly-Distribution arbeiten, haben also einen akuten Handlungsbedarf.
Hat sich bei den funktionalen Features in Keycloak mit dem Wechsel zu Quarkus nichts geändert, hat sich jedoch hinsichtlich des Deployments und der Konfiguration komplett einmal alles gedreht.
Keine Angst, es ist nicht alles komplizierter geworden.
Aber es ist alles anders.
Wer sich hierfür aber Zeit nimmt und die neuen (Migration-)Guides durcharbeitet, wird schnell merken, dass die Überarbeitung der Konfiguration durchaus Vorteile mit sich bringt.
Die ersten Migrations-Erfahrungen haben gezeigt, dass dies durchaus ein machbarer Schritt ist.
War es mit der Legacy-Version davon abhängig, wie man Keycloak betrieb (klassisches Deployment im Filesystem mit selbst zu erstellenden Scripten oder als Docker-Container, in dem bereits vorgefertigte Scripte enthalten waren), um die entsprechenden Konfigurationen auszuführen, ist es nun gleich in welcher Umgebung man sich befindet - Keycloak wird immer auf die gleiche Art und Weise konfiguriert.
Hierfür stehen dem Anwender drei Möglichkeiten zur Verfügung: eine .conf
Datei, in die die Parameter eingetragen werden, die Angabe der Parameter als Kommandozeilenargumente beim Start von Keycloak oder als Umgebungsvariablen.
Die Möglichkeiten können auch kombiniert miteinander eingesetzt werden.
Eines der spannendsten zukünftigen Features wird sein, dass Keycloak endlich eine Möglichkeit für Zero Downtime Upgrades bekommt. Bislang muss für ein Major-Version-Update immer der komplette Keycloak-Cluster runtergefahren und gestoppt werden, bevor mit einem einzigen Knoten die Migration und der Cluster wieder gestartet wird. Ursache hat dies hauptsächlich in der Verwendung von potenziellen breaking changes bei der Datenbank-Migration zwischen Major-Versionen. Diese Einschränkung will man zukünftig durch den Einsatz des neuen Map-Storage umgehen bzw. vermeiden. Zudem soll der Map-Storage auch noch für flexibler abzubildende und zu verwaltende (Meta-)Daten sorgen.
Fazit
Keycloak ist als Identity- & Access-Management sowie Single-Sign-On Server bereits in weiten Teilen im Einsatz und erfreut sich in den letzten Jahren immer weiterer Beliebtheit. Es wird von Unternehmen jeglicher Größenordnung eingesetzt, vom kleinen Startup bis hin zu internationalen Konzernen. Bietet es doch den großen Vorteil, viele out-of-the-box zu nutzenden Features direkt einsetzen, aber die Prozesse gleichzeitig auch an die eigenen Bedürfnisse anpassen und erweitern zu können. Und das bei einer vollständigen Hoheit über die gespeicherten Daten, da Keycloak nicht auf das Modell eines Managed-Services setzt und man dafür seine Daten aus der Hand geben muss, sondern über einen selbst zu verantwortenden Betrieb jederzeit über die Daten verfügen kann.
Auch wenn die OAuth2 und OIDC Spezifikationen nicht kompliziert zu verstehen sind und auf den ersten Blick recht einfach selbst zu implementieren wären, sollte man davon die Finger lassen und sich einer Anwendung bedienen, die das alles bereits abdeckt und implementiert. Keycloak macht das für uns. Und wir haben dafür den Kopf frei für die für uns wichtigen fachlichen Implementierungen. Schließlich sind wir eher nicht die expliziten Securityexperten.
Mit dem Wechsel auf das Quarkus-Application-Framework hat Keycloak gerade einen wichtigen Schritt unternommen, um auch in der Container-basierten Welt weiterhin vorne mitspielen und auch in Zukunft schnell neue Features implementieren und anbieten zu können. Es wird sich hier sicherlich noch vieles tun. Aber Stillstand heißt ja heute eher Rückschritt. Also freuen wir uns auf das, was mit Keycloak in Zukunft kommen wird!
Im nächsten Artikel schauen wir uns an, wie Keycloak einfach und sinnvoll an die eigenen (Unternehmens-)Anforderungen angepasst werden kann.
Du bist auf der Suche nach Keycloak Beratung, Unterstützung, Workshops oder Trainings?
Nimm Kontakt mit mir auf!« Keycloak 17 is out - Quarkus is now the default way to go! Keycloak an die eigenen Anforderungen anpassen und erweitern »