Keycloak Workflows: Inaktive Benutzer automatisch benachrichtigen, deaktivieren und löschen

September 22, 2026

Tags: #keycloak #workflows #iga #lifecycle-management #identity-governance

Benutzerkonten anzulegen ist einfach. Sie wieder loszuwerden, wenn sie niemand mehr braucht, ist es nicht. In vielen Umgebungen sammeln sich über die Jahre Konten an, die seit Monaten oder Jahren niemand mehr benutzt hat. Jedes dieser Konten ist ein potenzielles Einfallstor, und das Aufräumen bleibt meist an einem Admin hängen, der es “irgendwann mal” von Hand macht. Oder eben nicht.

Keycloak bringt mit den Workflows ein Feature mit, das genau solche Aufgaben automatisiert. In diesem Artikel zeige ich, was hinter dem Begriff IGA steckt, wie ein Workflow aufgebaut ist und wie ich damit inaktive Benutzer erst per Mail informiere, dann deaktiviere und schließlich lösche.

Was ist IGA?

IGA steht für Identity Governance and Administration. Gemeint ist ein Zusammenspiel aus Richtlinien, Prozessen und Technik, mit dem eine Organisation ihre Benutzerkonten verwaltet und steuert, wer worauf Zugriff hat.

Zwei Bereiche gehören dazu:

Für die Workflows in Keycloak ist der erste Teil der entscheidende: den Lebenszyklus der Benutzer in der eigenen Umgebung im Griff zu haben, und zwar automatisiert.

Kapitel "Managing workflows" im Server Administration Guide

Workflows in Keycloak: ein kurzer Rückblick

Die Workflows kamen mit Keycloak 26.4 als experimentelles Feature. Mit 26.5 wurden sie zum Preview-Feature, mit 26.6 dann offiziell supported. Dieser Artikel basiert auf Version 26.7.

Wer tiefer einsteigen möchte, findet zwei gute Quellen:

Workflows in der Admin Console

Leere Workflow-Übersicht in der Admin Console

In der Admin Console sind die Workflows der letzte Eintrag im linken Menü. Das Feature ist standardmäßig aktiviert, ich muss es also nicht erst über ein Feature-Flag einschalten. In einem neuen Realm ist die Liste natürlich leer.

Wer nach einem Klick auf Create workflow einen grafischen Editor mit Kästchen und Pfeilen erwartet, wird enttäuscht. Der Workflow-Editor ist schlicht ein Textfeld, in das ich eine Definition in YAML eintrage.

Eine kleine Randnotiz, weil ich es einfach witzig finde: Die UI wandelt das YAML in JSON um und schickt es an den Server. Der Server serialisiert das JSON zu einem String und speichert diesen in der Datenbank. Auf dem Rückweg liest der Server den String, macht daraus wieder ein Objekt, schickt es als JSON an die UI, und die UI wandelt es wieder in YAML um. Passend dazu steht über dem Feld “Workflow YAML” in der Oberfläche auch “Create a new workflow by providing its JSON representation”.

Anatomie eines Workflows

Mein Beispiel ist aus meiner Sicht der interessanteste Anwendungsfall: Benutzer, die sich eine gewisse Zeit lang nicht angemeldet haben, sollen erst informiert, dann deaktiviert und schließlich gelöscht werden.

Die vollständige Definition sieht so aus:

name: Disable inactive users
enabled: true
on: user_authenticated
concurrency:
  restart-in-progress: "true"
if: has-identity-provider-link(oidc)
steps:
  - uses: notify-user
    after: 20s
    with:
      subject: Please be aware of this!
      message: If you don't login again in the next time, your account will be deleted!
  - uses: disable-user
    after: 20s
  - uses: delete-user
    after: 20s
Workflow-Definition im YAML-Editor

Schauen wir uns die einzelnen Teile an.

name und enabled

name ist einfach der Name des Workflows, unter dem er in der Übersicht auftaucht.

enabled: true sorgt dafür, dass der Workflow direkt nach dem Speichern aktiv ist. Das Flag kann ich auch weglassen, denn ohne Angabe ist ein Workflow standardmäßig aktiviert.

on: das auslösende Ereignis

Mit on lege ich fest, auf welches Ereignis der Workflow reagiert. In meinem Fall ist das user_authenticated, also die erfolgreiche Anmeldung eines Benutzers.

Hier steckt ein wichtiger Punkt, den man verstanden haben muss: Ein Workflow läuft nach dem Anlegen nicht sofort für alle Benutzer im Realm los. Er wird erst gestartet, wenn das definierte Ereignis eintritt, und dann genau für den Benutzer, der das Ereignis ausgelöst hat. Solange sich niemand anmeldet, passiert also nichts.

if: die Bedingung

Mit if schränke ich ein, für welche Benutzer der Workflow überhaupt gilt. In meinem Beispiel sollen das nur Benutzer sein, die über einen bestimmten externen Identity Provider kommen:

if: has-identity-provider-link(oidc)

oidc ist dabei der Alias der Identity-Provider-Konfiguration in meinem Realm. So kann ich den Workflow exakt auf die Benutzer dieses einen IdPs zuschneiden.

Daneben gibt es weitere Bedingungen, z. B. ob ein Benutzer ein bestimmtes Attribut hat, Mitglied einer Gruppe ist oder eine Rolle zugewiesen bekommen hat. Lasse ich if komplett weg, gilt der Workflow für alle Benutzer, die das Ereignis auslösen.

steps: die Schrittkette

Unter steps definiere ich die Schritte, die nacheinander ausgeführt werden, optional mit einer zeitlichen Verzögerung. Jeder Schritt besteht aus:

In meinem Beispiel heißt das:

  1. notify-user: 20 Sekunden nach der Anmeldung bekommt der Benutzer eine Mail mit dem Hinweis, dass sein Konto gelöscht wird, wenn er sich nicht demnächst wieder anmeldet. Mit subject setze ich einen eigenen Betreff. Ohne subject verwendet Keycloak den Standard-Betreff “Account Disable Warning”.
  2. disable-user: 20 Sekunden nach der Mail wird der Benutzer deaktiviert.
  3. delete-user: Weitere 20 Sekunden später wird der Benutzer gelöscht.

Die 20 Sekunden sind natürlich nur für die Demo gewählt, ich möchte im Video ja nicht mehrere Tage warten. In Produktion reden wir hier eher über Tage oder Wochen als über Sekunden.

Für den Versand der Mail muss im Realm natürlich ein SMTP-Server konfiguriert sein.

concurrency: was passiert, wenn der Benutzer sich wieder anmeldet?

Bleibt noch dieser Teil:

concurrency:
  restart-in-progress: "true"

Er regelt, was passiert, wenn das auslösende Ereignis erneut eintritt, während der Workflow für diesen Benutzer noch läuft. Mit restart-in-progress: "true" beginnt der Workflow dann wieder von vorne.

Für mein Szenario ist genau das gewünscht: Bekommt ein Benutzer die Info-Mail und meldet sich daraufhin wieder an, wird er nicht 20 Sekunden später deaktiviert. Stattdessen startet der Workflow neu, und der Benutzer bekommt erst nach Ablauf der ersten Frist wieder eine Mail. Die Anmeldung setzt die “Inaktivitätsuhr” also zurück.

Ist der Benutzer allerdings bereits deaktiviert, kann er sich nicht mehr anmelden und den Workflow damit auch nicht mehr selbst zurücksetzen. Die Zeit zwischen Deaktivieren und Löschen ist das Zeitfenster, in dem ein Admin noch eingreifen, den Benutzer wieder aktivieren und den aktiven Workflow löschen kann.

Vorsicht mit delete-user

delete-user bedeutet wirklich löschen. Der Benutzer wird nicht in einen Status “gelöscht” versetzt, sondern aus der Datenbank entfernt. Er ist danach nicht mehr verfügbar. Diesen Schritt sollte man also mit Bedacht und mit ausreichend großzügigen Fristen einsetzen.

Speichern und Bearbeiten

Der angelegte Workflow in der Übersicht

Nach einem Klick auf Create erscheint der Workflow in der Übersicht und ist aktiv. Über den Namen komme ich jederzeit wieder in die Definition und kann sie bearbeiten.

Der Workflow in Aktion

Mein Testbenutzer jane.doe kommt aus einem externen Identity Provider mit dem Alias oidc. Vor dem ersten Login existiert er in Keycloak noch nicht, eine Suche nach “doe” in den Benutzern liefert also kein Ergebnis.

Nach der Anmeldung über den externen IdP legt Keycloak den Benutzer an, und das Ereignis user_authenticated startet den Workflow für jane.doe. Das sehe ich in den Benutzerdetails im Tab Workflows: Dort steht der Workflow “Disable inactive users” mit dem nächsten Schritt und darunter alle Schritte mit dem Zeitpunkt, zu dem sie geplant sind.

Tab "Workflows" in den Benutzerdetails mit den geplanten Schritten

Nach 20 Sekunden ist notify-user erledigt, der nächste Schritt ist disable-user. Im Postfach liegt die Mail mit meinem eigenen Betreff “Please be aware of this!” und dem Text aus der Workflow-Definition.

Zum Vergleich: Eine Mail aus einem früheren Test ohne subject kommt mit dem Standard-Betreff “Account Disable Warning”.

Weitere 20 Sekunden später ist jane.doe deaktiviert und kann sich nicht mehr anmelden. Der Benutzer existiert aber noch, der nächste Schritt ist delete-user.

Deaktivierter Benutzer, nächster Schritt delete-user

Nach noch einmal 20 Sekunden ist der Benutzer weg. Ein Reload der Benutzerdetails endet mit der Fehlermeldung “user not found”, und auch die Suche nach “doe” findet niemanden mehr.

Der Neustart bei erneutem Login

Im zweiten Durchlauf melde ich mich wieder mit jane.doe an. Nachdem die Info-Mail angekommen ist und als nächster Schritt disable-user ansteht, melde ich mich noch einmal an. Durch restart-in-progress wird der laufende Workflow zurückgesetzt, und als nächster Schritt steht wieder notify-user an, 20 Sekunden später kommt erneut eine Mail. Lasse ich den Benutzer danach in Ruhe, läuft der Workflow wie gehabt bis zum Deaktivieren und Löschen durch.

Nach erneutem Login ist wieder notify-user der nächste Schritt

Zur Gegenprobe ein Blick auf meinen regulären Benutzer dasniko: Der hat sich seit dem Anlegen des Workflows nicht angemeldet, im Tab Workflows steht deshalb auch nichts. Der Workflow startet eben nur über das Ereignis, das ich in der Definition festgelegt habe.

Was Workflows sonst noch können

Mein Beispiel deckt nur einen kleinen Teil ab. Die Dokumentation beschreibt alle Möglichkeiten im Detail, hier die wichtigsten im Überblick.

Ereignisse

Neben user_authenticated stehen weitere Ereignisse zur Verfügung:

Ereignis Wann Parameter
user_created Benutzer wird im Realm angelegt –
user_authenticated Benutzer meldet sich am Realm an Client-ID des Clients, über den die Anmeldung erfolgt
user_federated_identity_added Benutzer wird föderiert bzw. mit einem IdP verknüpft Alias des Identity Providers
user_federated_identity_removed Verknüpfung mit einem IdP wird entfernt Alias des Identity Providers
user_role_granted Benutzer bekommt eine Rolle Rollenname
user_role_revoked Rolle wird entzogen Rollenname
user_group_membership_added Benutzer tritt einer Gruppe bei Gruppenname oder -pfad
user_group_membership_removed Benutzer wird aus einer Gruppe entfernt Gruppenname oder -pfad
client_created Client wird im Realm angelegt –
client_authenticated Client authentifiziert sich (z. B. Client Credentials Grant) –
Tabelle der Event-Funktionen in der Dokumentation

Ereignisse sind Funktionen in einem Ausdruck und können deshalb Parameter haben. Ein Workflow, der nur beim Beitritt zu einer bestimmten Gruppe startet, sieht z. B. so aus:

on: user_group_membership_added(/mygroup)

Damit könnte ich einem Benutzer etwa eine Mail schicken, sobald er einer neuen Gruppe hinzugefügt wurde.

Mehrere Ereignisse lassen sich auch logisch verknüpfen:

on: user_created or user_authenticated

Zeitgesteuerte Workflows

Statt auf ein Ereignis zu reagieren, kann ein Workflow auch periodisch laufen. Dafür gibt es die Einstellung schedule:

name: Track inactive users
schedule:
  after: 5s
  batch-size: 100
Abschnitt "Scheduling workflows" in der Dokumentation

after gibt das Intervall zwischen zwei Läufen an, batch-size die maximale Anzahl an Realm-Ressourcen, die pro Lauf verarbeitet werden. Bei jedem Lauf sucht die Workflow-Engine nach den Ressourcen, auf die die Bedingung zutrifft, und startet für sie den Workflow. Ohne Bedingung gilt das für alle Ressourcen.

Das ist auch die Antwort auf eine naheliegende Frage zu meinem Beispiel: Ein ereignisbasierter Workflow erfasst nur Benutzer, die das Ereignis auslösen. Wer sich nach dem Anlegen des Workflows nie wieder anmeldet, bekommt von einem user_authenticated-Workflow nichts mit, denn ein Ereignis für “hat sich nicht angemeldet” gibt es nicht. Mit schedule kann ich dagegen auch Benutzer erfassen und bearbeiten, für die gar kein Ereignis eintritt.

Bedingungen

Für if stehen u. a. diese Bedingungen zur Verfügung:

Schritte

Tabelle der User-Steps in der Dokumentation

Die eingebauten Schritte beziehen sich jeweils auf die Ressource, die mit dem Ereignis verknüpft ist. Für Benutzer gibt es:

Für Clients gibt es entsprechend disable-client und delete-client zum Deaktivieren und Löschen.

Sofortige und geplante Schritte

after akzeptiert Zeitangaben in Millisekunden, Sekunden, Minuten, Stunden oder Tagen, also z. B. 20s oder 7d. Lasse ich after weg, wird der Schritt sofort ausgeführt. Hier laufen beide Schritte direkt nacheinander:

steps:
  - uses: notify-user
  - uses: disable-user

Und hier wird der Benutzer sieben Tage nach der Benachrichtigung deaktiviert:

steps:
  - uses: notify-user
  - uses: disable-user
    after: 7d

Ausgeführt werden die Schritte von der Workflow-Engine asynchron und losgelöst von dem Request, der den Workflow ausgelöst hat. Eine Anmeldung wird also nicht dadurch langsamer, dass im Hintergrund ein Workflow startet.

Fazit

Workflows sind ein einfaches, aber wirkungsvolles Werkzeug, um den Lebenszyklus von Benutzern in Keycloak zu automatisieren. Ereignis, Bedingung, Schrittkette: Mehr braucht es nicht, um Aufgaben zu erledigen, die sonst manuell und damit oft gar nicht passieren.

Mein Tipp: Lest das Kapitel in der Dokumentation in Ruhe durch, spielt mit den Ereignissen, Bedingungen und Schritten in einer Testumgebung und konfiguriert dann die Workflows, die zu eurer Umgebung passen. Und haltet eure Keycloak-Installation aktuell, das Feature entwickelt sich gerade noch weiter.

Alle Schritte aus diesem Artikel zeige ich auch im Video auf meinem YouTube-Kanal.

Wenn ihr Workflows oder andere Aspekte des Identity Lifecycle Managements mit Keycloak in eurer Organisation einführen wollt, unterstütze ich euch gerne mit Beratung und Trainings.

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

Nimm Kontakt mit mir auf!

« Delegated User Management with Keycloak: Fine-Grained Admin Permissions (FGAP v2) in Practice Keycloak Workflows: Automatically Notify, Disable and Delete Inactive Users »