Keycloak Workflows: Automatically Notify, Disable and Delete Inactive Users

September 22, 2026

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

Creating user accounts is easy. Getting rid of them once nobody needs them anymore is not. In many environments, accounts pile up over the years that nobody has used for months or even years. Each of these accounts is a potential entry point, and the cleanup is usually left to an admin who will do it manually “at some point”. Or not at all.

With Workflows, Keycloak offers a feature that automates exactly these kinds of tasks. In this article, I show what the term IGA is about, how a workflow is structured, and how I use it to first notify inactive users by email, then disable them, and finally delete them.

What is IGA?

IGA stands for Identity Governance and Administration. It describes a combination of policies, processes and technology an organization uses to manage its user accounts and to control who has access to what.

It covers two areas:

For Keycloak Workflows, the first part is the one that matters: keeping the lifecycle of the users in your environment under control, and doing it automatically.

Chapter "Managing workflows" in the Server Administration Guide

Workflows in Keycloak: a short history

Workflows were introduced in Keycloak 26.4 as an experimental feature. With 26.5 they became a preview feature, and with 26.6 they are officially supported. This article is based on version 26.7.

If you want to dig deeper, there are two good sources:

Workflows in the Admin Console

Empty workflow overview in the Admin Console

In the Admin Console, Workflows is the last entry in the left-hand menu. The feature is enabled by default, so I don’t have to switch it on with a feature flag first. In a new realm, the list is empty, of course.

If you click Create workflow and expect a graphical editor with boxes and arrows, you’ll be disappointed. The workflow editor is simply a text field where I enter a definition in YAML.

A small side note, because I just find it funny: The UI converts the YAML to JSON and sends it to the server. The server serializes the JSON into a string and stores it in the database. On the way back, the server reads the string, turns it into an object again, sends it to the UI as JSON, and the UI converts it back to YAML. Fittingly, the UI says “Create a new workflow by providing its JSON representation” right above the field labeled “Workflow YAML”.

Anatomy of a workflow

My example is, in my opinion, the most interesting use case: users who haven’t logged in for a certain amount of time should first be notified, then disabled, and finally deleted.

The complete definition looks like this:

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 in the YAML editor

Let’s look at the individual parts.

name and enabled

name is simply the name of the workflow as it appears in the overview.

enabled: true makes sure the workflow is active right after saving. I can also omit the flag, because a workflow is enabled by default.

on: the triggering event

With on, I define which event the workflow reacts to. In my case, this is user_authenticated, i.e. a successful user login.

There’s an important point here that you need to understand: After creating a workflow, it does not immediately start running for all users in the realm. It is only started when the defined event occurs, and then exactly for the user who triggered the event. As long as nobody logs in, nothing happens.

if: the condition

With if, I restrict which users the workflow applies to. In my example, these should only be users coming from a specific external identity provider:

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

oidc is the alias of the identity provider configuration in my realm. This lets me scope the workflow exactly to the users of this one IdP.

There are further conditions as well, e.g. whether a user has a certain attribute, is a member of a group, or has been assigned a role. If I omit if entirely, the workflow applies to all users who trigger the event.

steps: the step chain

Under steps, I define the steps that are executed one after another, optionally with a time delay. Each step consists of:

In my example, this means:

  1. notify-user: 20 seconds after login, the user receives an email saying the account will be deleted unless they log in again soon. With subject, I set a custom subject. Without subject, Keycloak uses the default subject “Account Disable Warning”.
  2. disable-user: 20 seconds after the email, the user is disabled.
  3. delete-user: Another 20 seconds later, the user is deleted.

The 20 seconds are, of course, only chosen for the demo; I don’t want to wait several days in the video. In production, we’re talking about days or weeks rather than seconds.

To send the email, an SMTP server must of course be configured in the realm.

concurrency: what happens when the user logs in again?

This part is still left:

concurrency:
  restart-in-progress: "true"

It controls what happens when the triggering event occurs again while the workflow is still running for this user. With restart-in-progress: "true", the workflow starts over from the beginning.

That’s exactly what I want in my scenario: If a user receives the notification email and then logs in again, they won’t be disabled 20 seconds later. Instead, the workflow restarts, and the user only gets another email after the first period has elapsed. So the login resets the “inactivity clock”.

However, once the user is disabled, they can no longer log in and therefore can’t reset the workflow themselves. The time between disabling and deleting is the window in which an admin can still step in, re-enable the user and delete the active workflow.

Be careful with delete-user

delete-user really means delete. The user is not put into a “deleted” state but removed from the database. Afterwards, the user is gone. So use this step with care and with sufficiently generous time periods.

Saving and editing

The created workflow in the overview

After clicking Create, the workflow appears in the overview and is active. Via its name, I can get back into the definition at any time and edit it.

The workflow in action

My test user jane.doe comes from an external identity provider with the alias oidc. Before the first login, the user doesn’t exist in Keycloak yet, so searching the users for “doe” returns no results.

After logging in via the external IdP, Keycloak creates the user, and the user_authenticated event starts the workflow for jane.doe. I can see this in the user details on the Workflows tab: It lists the “Disable inactive users” workflow with its next step, and below that all steps with the time they are scheduled for.

Workflows tab in the user details with the scheduled steps

After 20 seconds, notify-user is done and the next step is disable-user. The inbox contains the email with my custom subject “Please be aware of this!” and the text from the workflow definition.

For comparison: an email from an earlier test without subject arrives with the default subject “Account Disable Warning”.

Another 20 seconds later, jane.doe is disabled and can no longer log in. The user still exists, though; the next step is delete-user.

Disabled user, next step delete-user

After yet another 20 seconds, the user is gone. Reloading the user details ends with the error message “user not found”, and searching for “doe” doesn’t find anyone anymore.

Restart on repeated login

In the second run, I log in with jane.doe again. Once the notification email has arrived and disable-user is the next step, I log in once more. Thanks to restart-in-progress, the running workflow is reset, notify-user is the next step again, and 20 seconds later another email arrives. If I leave the user alone after that, the workflow runs through to disabling and deleting as before.

After the repeated login, notify-user is the next step again

As a cross-check, a look at my regular user dasniko: This user hasn’t logged in since the workflow was created, so the Workflows tab is empty. The workflow is only started by the event I defined in the definition.

What else workflows can do

My example covers only a small part. The documentation describes all options in detail; here’s an overview of the most important ones.

Events

Besides user_authenticated, further events are available:

Event When Parameter
user_created User is added to the realm –
user_authenticated User authenticates to the realm Client ID of the client acting on behalf of the user
user_federated_identity_added User is federated or linked to an IdP Identity provider alias
user_federated_identity_removed Link to an IdP is removed Identity provider alias
user_role_granted A role is granted to the user Role name
user_role_revoked A role is revoked from the user Role name
user_group_membership_added User joins a group Group name or path
user_group_membership_removed User is removed from a group Group name or path
client_created Client is added to the realm –
client_authenticated Client authenticates (e.g. client credentials grant) –
Table of event functions in the documentation

Events are functions in an expression and can therefore take parameters. A workflow that only starts when a user joins a specific group looks like this, for example:

on: user_group_membership_added(/mygroup)

With this, I could send a user an email as soon as they’ve been added to a new group.

Multiple events can also be combined logically:

on: user_created or user_authenticated

Scheduled workflows

Instead of reacting to an event, a workflow can also run periodically. This is done with the schedule setting:

name: Track inactive users
schedule:
  after: 5s
  batch-size: 100
Section "Scheduling workflows" in the documentation

after defines the interval between two runs, batch-size the maximum number of realm resources processed per run. On each run, the workflow engine looks for the resources matching the condition and starts the workflow for them. Without a condition, this applies to all resources.

This also answers an obvious question about my example: An event-based workflow only covers users who trigger the event. Someone who never logs in again after the workflow has been created is never picked up by a user_authenticated workflow, because there is no event for “has not logged in”. With schedule, on the other hand, I can also pick up and process users for whom no event occurs at all.

Conditions

Among others, these conditions are available for if:

Steps

Table of user steps in the documentation

The built-in steps always target the resource associated with the event. For users, there are:

For clients, there are accordingly disable-client and delete-client for disabling and deleting.

Immediate and scheduled steps

after accepts durations in milliseconds, seconds, minutes, hours or days, e.g. 20s or 7d. If I omit after, the step is executed immediately. Here, both steps run directly one after the other:

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

And here, the user is disabled seven days after being notified:

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

The workflow engine executes the steps asynchronously, detached from the request that triggered the workflow. So a login doesn’t get slower just because a workflow starts in the background.

Conclusion

Workflows are a simple yet effective tool for automating the user lifecycle in Keycloak. Event, condition, step chain: that’s all it takes to handle tasks that would otherwise be done manually, and therefore often not at all.

My advice: read the chapter in the documentation carefully, play around with events, conditions and steps in a test environment, and then configure the workflows that fit your environment. And keep your Keycloak installation up to date, as the feature is still evolving.

I also show all steps from this article in the video on my YouTube channel.

If you want to introduce workflows or other aspects of identity lifecycle management with Keycloak in your organization, I’m happy to support you with consulting and training.

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

Nimm Kontakt mit mir auf!

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