./kc.sh start-dev --features=workflows --spi-events-listener--workflow-event-listener--step-runner-task-interval=1000 --log-level="INFO,org.keycloak.models.workflow:DEBUG"
October 01 2025 by Stefan Guilhen
At its core, the Workflows feature is an automation engine for administrative tasks. It empowers Keycloak administrators to define a series of steps that run automatically in response to specific events. The primary motivation behind this feature is to build a robust identity governance model within Keycloak while reducing manual, repetitive work.
In any organization, managing the lifecycle of users and other resources is a critical but often time-consuming task. For example, failing to disable or remove inactive user accounts can create significant security vulnerabilities. Similarly, manually onboarding new users to specific roles or groups is inefficient and prone to error. Workflows aim to solve these problems by providing a flexible framework to automate these essential processes.
The introduction of Workflows is a significant step forward for Keycloak in the realm of Identity Governance and Administration (IGA). IGA is a policy-based approach to identity management and access control that helps organizations strengthen security, meet compliance requirements, and improve operational efficiency.
By allowing administrators to define automated processes for managing the user lifecycle, Workflows directly address key IGA principles. This automation not only enhances security by addressing inactive accounts but also reduces the administrative burden on processes that can be fully automated, like onboarding and offboarding users.
In its initial experimental release, the Workflows feature focuses on the following core capabilities:
User-Centric Automation: In this first iteration, the feature is primarily targeted at automating tasks related to the management of user resources. However, the concept is designed to be extensible and could be enhanced in the future to manage other realm components like clients, organizations, or identity providers.
Event-Driven Triggers: Workflows are triggered by events within the realm. This includes not only user-triggered events, such as logins, but also administrative events, such as a user being added to a group or being assigned a role.
Configurable Steps and Conditions: A workflow consists of one or more steps that are executed in sequence if the conditions are satisfied. The conditions are evaluated in addition to the event trigger, allowing for the definition of workflows for resources that match specific set of conditions (e.g. users not in the 'admin' role or users from a specific identity provider).
Schedulable Steps: Steps can be configured to run immediately or after a specified delay. This is particularly useful for scenarios like notifying users of impending account deactivation after a period of inactivity.
The steps and conditions have their own SPIs, allowing for custom implementations to be plugged in if the built-in options do not meet your
requirements. Implementations are referenced by their providerId
in the respective uses
property in the workflow definition - more on that in the
example below.
For the first iteration, the goal was to provide a set of capabilities to allow administrators to detect and act on inactive accounts. To achieve that, the following built-in steps can be configured within a workflow:
notify-user: Sends automated email notifications to users to inform their accounts can be disabled/deleted.
disable-user: Disables the user account.
remove user: Automatically removes an account from the system.
Future release will introduce other built-in steps to aid in onboarding and offboarding users, such as join/leave groups, assign/unassign roles, add/remove user attributes, join/leave organizations, etc.
As for the events that can trigger a workflow, the following are supported in this first release:
USER_LOGIN: Triggered when a user successfully logs in
USER_ADD: Triggered when a user is created or registered
USER_GROUP_MEMBERSHIP_ADD: Triggered when a user is added to a group
USER_ROLE_ADD: Triggered when a user is assigned a role
The conditions that can be used to filter the resources that will be affected by a workflow include:
is-member-of(group): Checks if a user is a member of a specific group
has-role(role): Checks if a user has a specific role
has-user-attribute(key, value): Checks if a user has a specific attribute with a given value
has-identity-provider-link(identity-provider): Checks if a user is linked to a specific identity provider
Let’s walk through a practical example of how to use the feature by setting up a workflow that notifies and disables users who are not admins after some period of inactivity. For the purposes of this demonstration, we will use short timespans, but in a real-world scenario, you would likely use longer periods (e.g., 60 days, 90 days, etc).
The first step is to enable the feature when starting Keycloak:
./kc.sh start-dev --features=workflows --spi-events-listener--workflow-event-listener--step-runner-task-interval=1000 --log-level="INFO,org.keycloak.models.workflow:DEBUG"
The step-runner-task-interval
configuration is optional and is used to change the interval at which the background task that executes
the scheduled steps runs. By default, it is set to 12 hours, but for demonstration purposes we are setting it to run every second.
The log-level
was also adjusted so we can see what is happening during the execution of the workflow.
As this is an experimental feature, it is recommended that you test it using a new realm in a non-production environment.
Access the Admin Console, then create a new realm and add two users to it:
User 1: alice
(no special roles)
User 2: bob
(assign the realm-management/realm-admin
role to this user)
Make sure to set up the email settings for the realm so the notification step can send emails.
In the new realm, navigate to the Workflows section under the Configure menu:
Click on Create Workflow to define a new workflow. At this moment the UI is very simple and takes a JSON
representation of the workflow as input.
Paste the following JSON
into the editor to create the test workflow:
{
"name": "disable inactive users",
"uses": "event-based-workflow",
"on": "USER_LOGIN",
"reset-on": "USER_LOGIN",
"if": [
{
"uses":"expression",
"with": {
"expression": "!has-role(\"realm-management/realm-admin\")"
}
}
],
"steps":[
{
"uses":"notify-user",
"after": "30000",
"with":{
"custom_message": "Your account can be disabled due to inactivity!"
}
},
{
"uses":"disable-user",
"after":"30000"
}
]
}
Click on Save to save the workflow.
Most of the workflow definition is self-explanatory, but it is worth highlighting a few points:
The reset-on
property is also set to USER_LOGIN
, which means that if the user logs in again before the workflow completes, it will be reset and started over.
This is important when we track inactive users, as we want to reset the workflow if they become active again.
As mentioned before, the uses
property references the providerId
of the step or condition implementation to be used.
The condition is using an expression to check if the user does not have the realm-admin
role. The expression can be used to
combine multiple conditions using the logical operators AND, OR, !(NOT) and parentheses. So it is possible to do something like
!has-role("admin") AND has-user-attribute("department","engineering")
.
The steps can have an after
property that defines when the step should run. The value is always relative to the previous step,
so in the example above, the disable-user
step will run 30 seconds after the notify-user
step completes.
Additional properties of steps can be specified in the with
section. In the example above, we are customizing the notification email message.
Go to the account console (realms/{your-realm}/account
) and log in as alice
. You should see the following in the server logs:
2025-10-01 12:33:46,320 DEBUG [org.keycloak.models.workflow.WorkflowExecutionContext] (executor-thread-37) Started workflow 'disable inactive users' for resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:33:46,320 DEBUG [org.keycloak.models.workflow.WorkflowsManager] (executor-thread-37) Scheduling step notify-user to run in 30000 ms for resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
This indicates the workflow was activated for alice when they logged in as they do not have the realm-admin
role.
If you wait 30 seconds, the notification email should be sent, and you should see the following in the logs:
2025-10-01 12:34:16,425 DEBUG [org.keycloak.models.workflow.WorkflowsManager] (Timer-0) Running step notify-user on resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:34:16,433 DEBUG [org.keycloak.models.workflow.NotifyUserStepProvider] (Timer-0) Notification email sent to user alice (alice@keycloak.org)
2025-10-01 12:34:16,433 DEBUG [org.keycloak.models.workflow.WorkflowExecutionContext] (Timer-0) Step notify-user completed successfully (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:34:16,433 DEBUG [org.keycloak.models.workflow.WorkflowsManager] (Timer-0) Scheduling step disable-user to run in 30000 ms for resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
At this point, the notification step was executed, and the disable step was scheduled to run in another 30 seconds. To simulate alice
reacting to the e-mail, we will reload the account page to force a new login, which should reset the workflow as seen in the logs:
2025-10-01 12:34:46,997 DEBUG [org.keycloak.models.workflow.WorkflowExecutionContext] (executor-thread-39) Restarted workflow 'disable inactive users' for resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:34:46,997 DEBUG [org.keycloak.models.workflow.WorkflowsManager] (executor-thread-39) Scheduling step notify-user to run in 30000 ms for resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
As can be seen, once alice
became active again, the workflow was restarted, and the steps were rescheduled.
If we now wait for a whole minute, we should see the entire workflow executing:
2025-10-01 12:35:17,430 DEBUG [org.keycloak.models.workflow.WorkflowsManager] (Timer-0) Running step notify-user on resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:35:17,441 DEBUG [org.keycloak.models.workflow.WorkflowExecutionContext] (Timer-0) Step notify-user completed successfully (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:35:17,442 DEBUG [org.keycloak.models.workflow.WorkflowsManager] (Timer-0) Scheduling step disable-user to run in 30000 ms for resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:35:48,435 DEBUG [org.keycloak.models.workflow.WorkflowsManager] (Timer-0) Running step disable-user on resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:35:48,436 DEBUG [org.keycloak.models.workflow.WorkflowExecutionContext] (Timer-0) Step disable-user completed successfully (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
2025-10-01 12:35:48,436 DEBUG [org.keycloak.models.workflow.WorkflowExecutionContext] (Timer-0) Workflow 'disable inactive users' completed for resource 8bddd017-5e0d-493d-a8d5-a657721299e4 (execution id: ea42006c-b7e1-421a-b6dc-44ece45f4011)
At this point, the workflow has completed, and `alice’s account has been disabled.
Repeating the steps with user bob
should show that the workflow is not activated for this user, as they have the realm-admin
role.
So nothing should be printed in the logs when bob
logs in.
The roadmap for the Workflows feature is to get it to supported status in Keycloak 26.5. It is under active development, so it is possible that some details shown in this blog post change before it becomes supported. Among the planned improvements are:
Additional built-in steps to cover more use cases, particularly around onboarding and offboarding users.
Additional events that can trigger workflows, such as USER_UPDATED, USER_GROUP_MEMBERSHIP_REMOVE, USER_ROLE_REMOVE, USER_ORGANIZATION_ADD/REMOVE, etc.
Support for workflow templates to simplify the creation of common workflows.
Support for workflows in YAML format in addition to JSON.
Allow admins to assign workflows to existing resources, not only have them triggered by events.
Improvements to the UI.
Quality of life improvements - e.g., ability to use 30d
or 12h
instead of the time in milliseconds, along with other simplifications
to the workflow definition.
Allow better control over the background task that runs the scheduled steps, going beyond just the time interval and allowing configuration of the exact time of day it runs, etc.
You can follow the progress of the feature on GitHub: https://github.com/keycloak/keycloak/issues/39888.
We highly encourage our users to try out the feature and provide feedback. We’ve opened a dedicated discussion thread in Github, so feel free to comment/discuss the features there or even in the Epic linked above. As usual, contributions and feedback are more than welcome! Let’s together make Keycloak even better with Workflows!