Keycloak

Since Camel 4.15

Both producer and consumer are supported

The Keycloak component supports running operations on Keycloak instance and policy enforcements.

Component Features

The Keycloak component provides two main functionalities:

  1. Producer Operations - Manage Keycloak instances via the Admin API (realms, users, roles, clients)

  2. Security Policies - Route-level authorization using Keycloak authentication and authorization services

URI Format

keycloak://label[?options]

You can append query options to the URI in the following format:

?options=value&option2=value&…​

Configuring Options

Camel components are configured on two separate levels:

  • component level

  • endpoint level

Configuring Component Options

At the component level, you set general and shared configurations that are, then, inherited by the endpoints. It is the highest configuration level.

For example, a component may have security settings, credentials for authentication, urls for network connection and so forth.

Some components only have a few options, and others may have many. Because components typically have pre-configured defaults that are commonly used, then you may often only need to configure a few options on a component; or none at all.

You can configure components using:

  • the Component DSL.

  • in a configuration file (application.properties, *.yaml files, etc).

  • directly in the Java code.

Configuring Endpoint Options

You usually spend more time setting up endpoints because they have many options. These options help you customize what you want the endpoint to do. The options are also categorized into whether the endpoint is used as a consumer (from), as a producer (to), or both.

Configuring endpoints is most often done directly in the endpoint URI as path and query parameters. You can also use the Endpoint DSL and DataFormat DSL as a type safe way of configuring endpoints and data formats in Java.

A good practice when configuring options is to use Property Placeholders.

Property placeholders provide a few benefits:

  • They help prevent using hardcoded urls, port numbers, sensitive information, and other settings.

  • They allow externalizing the configuration from the code.

  • They help the code to become more flexible and reusable.

The following two sections list all the options, firstly for the component followed by the endpoint.

Component Options

The Keycloak component supports 30 options, which are listed below.

Name Description Default Type

accessToken (common)

Pre-obtained access token for authentication. When provided, this token will be used directly instead of obtaining one through username/password or client credentials flow.

String

authClient (common)

Filter admin events by authentication client ID.

String

authIpAddress (common)

Filter admin events by authentication IP address.

String

authRealm (common)

Keycloak realm to authenticate against. If not specified, the realm parameter is used for authentication. This is useful when you want to authenticate against one realm (e.g., master) but perform operations on another realm.

master

String

authRealmFilter (common)

Filter admin events by authentication realm.

String

authUser (common)

Filter admin events by authentication user ID.

String

client (common)

Filter events by client ID.

String

clientId (common)

Keycloak client ID.

String

clientSecret (common)

Keycloak client secret.

String

configuration (common)

Component configuration.

KeycloakConfiguration

dateFrom (common)

Filter events by start date/time in milliseconds since epoch.

String

dateTo (common)

Filter events by end date/time in milliseconds since epoch.

String

eventType (common)

Type of events to consume: events or admin-events.

events

String

first (common)

Offset for pagination (first result index).

0

int

ipAddress (common)

Filter events by IP address.

String

keycloakClient (common)

Autowired To use an existing configured Keycloak admin client.

Keycloak

maxResults (common)

Maximum number of events to retrieve per poll.

100

int

operation (common)

The operation to perform.

Enum values:

  • createRealm

  • deleteRealm

  • getRealm

  • updateRealm

  • createUser

  • deleteUser

  • getUser

  • updateUser

  • listUsers

  • searchUsers

  • createRole

  • deleteRole

  • getRole

  • updateRole

  • listRoles

  • assignRoleToUser

  • removeRoleFromUser

  • getUserRoles

  • createGroup

  • deleteGroup

  • getGroup

  • updateGroup

  • listGroups

  • addUserToGroup

  • removeUserFromGroup

  • listUserGroups

  • createClient

  • deleteClient

  • getClient

  • updateClient

  • listClients

  • resetUserPassword

  • createClientRole

  • deleteClientRole

  • getClientRole

  • updateClientRole

  • listClientRoles

  • assignClientRoleToUser

  • removeClientRoleFromUser

  • listUserSessions

  • logoutUser

  • createClientScope

  • deleteClientScope

  • getClientScope

  • updateClientScope

  • listClientScopes

  • createIdentityProvider

  • deleteIdentityProvider

  • getIdentityProvider

  • updateIdentityProvider

  • listIdentityProviders

  • createResource

  • deleteResource

  • getResource

  • updateResource

  • listResources

  • createResourcePolicy

  • deleteResourcePolicy

  • getResourcePolicy

  • updateResourcePolicy

  • listResourcePolicies

  • createResourcePermission

  • deleteResourcePermission

  • getResourcePermission

  • updateResourcePermission

  • listResourcePermissions

  • evaluatePermission

  • getUserAttributes

  • setUserAttribute

  • deleteUserAttribute

  • getUserCredentials

  • deleteUserCredential

  • sendVerifyEmail

  • sendPasswordResetEmail

  • addRequiredAction

  • removeRequiredAction

  • executeActionsEmail

  • getClientSecret

  • regenerateClientSecret

KeycloakOperations

operationTypes (common)

Filter admin events by operation types (comma-separated list, e.g., CREATE,UPDATE,DELETE).

String

password (common)

Keycloak password.

String

pojoRequest (common)

If we want to use a POJO request as body or not.

false

boolean

realm (common)

Keycloak realm, the default is master because usually all the operations are done starting from the master realm.

master

String

resourcePath (common)

Filter admin events by resource path.

String

serverUrl (common)

Keycloak server URL.

String

types (common)

Filter events by event types (comma-separated list, e.g., LOGIN,LOGOUT).

String

user (common)

Filter events by user ID.

String

username (common)

Keycloak username.

String

bridgeErrorHandler (consumer)

Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored.

false

boolean

lazyStartProducer (producer)

Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel’s routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing.

false

boolean

autowiredEnabled (advanced)

Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc.

true

boolean

Endpoint Options

The Keycloak endpoint is configured using URI syntax:

keycloak:label

With the following path and query parameters:

Path Parameters (1 parameters)

Name Description Default Type

label (common)

Required Logical name.

String

Query Parameters (46 parameters)

Name Description Default Type

accessToken (common)

Pre-obtained access token for authentication. When provided, this token will be used directly instead of obtaining one through username/password or client credentials flow.

String

authClient (common)

Filter admin events by authentication client ID.

String

authIpAddress (common)

Filter admin events by authentication IP address.

String

authRealm (common)

Keycloak realm to authenticate against. If not specified, the realm parameter is used for authentication. This is useful when you want to authenticate against one realm (e.g., master) but perform operations on another realm.

master

String

authRealmFilter (common)

Filter admin events by authentication realm.

String

authUser (common)

Filter admin events by authentication user ID.

String

client (common)

Filter events by client ID.

String

clientId (common)

Keycloak client ID.

String

clientSecret (common)

Keycloak client secret.

String

dateFrom (common)

Filter events by start date/time in milliseconds since epoch.

String

dateTo (common)

Filter events by end date/time in milliseconds since epoch.

String

eventType (common)

Type of events to consume: events or admin-events.

events

String

first (common)

Offset for pagination (first result index).

0

int

ipAddress (common)

Filter events by IP address.

String

keycloakClient (common)

Autowired To use an existing configured Keycloak admin client.

Keycloak

maxResults (common)

Maximum number of events to retrieve per poll.

100

int

operation (common)

The operation to perform.

Enum values:

  • createRealm

  • deleteRealm

  • getRealm

  • updateRealm

  • createUser

  • deleteUser

  • getUser

  • updateUser

  • listUsers

  • searchUsers

  • createRole

  • deleteRole

  • getRole

  • updateRole

  • listRoles

  • assignRoleToUser

  • removeRoleFromUser

  • getUserRoles

  • createGroup

  • deleteGroup

  • getGroup

  • updateGroup

  • listGroups

  • addUserToGroup

  • removeUserFromGroup

  • listUserGroups

  • createClient

  • deleteClient

  • getClient

  • updateClient

  • listClients

  • resetUserPassword

  • createClientRole

  • deleteClientRole

  • getClientRole

  • updateClientRole

  • listClientRoles

  • assignClientRoleToUser

  • removeClientRoleFromUser

  • listUserSessions

  • logoutUser

  • createClientScope

  • deleteClientScope

  • getClientScope

  • updateClientScope

  • listClientScopes

  • createIdentityProvider

  • deleteIdentityProvider

  • getIdentityProvider

  • updateIdentityProvider

  • listIdentityProviders

  • createResource

  • deleteResource

  • getResource

  • updateResource

  • listResources

  • createResourcePolicy

  • deleteResourcePolicy

  • getResourcePolicy

  • updateResourcePolicy

  • listResourcePolicies

  • createResourcePermission

  • deleteResourcePermission

  • getResourcePermission

  • updateResourcePermission

  • listResourcePermissions

  • evaluatePermission

  • getUserAttributes

  • setUserAttribute

  • deleteUserAttribute

  • getUserCredentials

  • deleteUserCredential

  • sendVerifyEmail

  • sendPasswordResetEmail

  • addRequiredAction

  • removeRequiredAction

  • executeActionsEmail

  • getClientSecret

  • regenerateClientSecret

KeycloakOperations

operationTypes (common)

Filter admin events by operation types (comma-separated list, e.g., CREATE,UPDATE,DELETE).

String

password (common)

Keycloak password.

String

pojoRequest (common)

If we want to use a POJO request as body or not.

false

boolean

realm (common)

Keycloak realm, the default is master because usually all the operations are done starting from the master realm.

master

String

resourcePath (common)

Filter admin events by resource path.

String

serverUrl (common)

Keycloak server URL.

String

types (common)

Filter events by event types (comma-separated list, e.g., LOGIN,LOGOUT).

String

user (common)

Filter events by user ID.

String

username (common)

Keycloak username.

String

sendEmptyMessageWhenIdle (consumer)

If the polling consumer did not poll any files, you can enable this option to send an empty message (no body) instead.

false

boolean

bridgeErrorHandler (consumer (advanced))

Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored.

false

boolean

exceptionHandler (consumer (advanced))

To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the consumer will deal with exceptions, that will be logged at WARN or ERROR level and ignored.

ExceptionHandler

exchangePattern (consumer (advanced))

Sets the exchange pattern when the consumer creates an exchange.

Enum values:

  • InOnly

  • InOut

ExchangePattern

pollStrategy (consumer (advanced))

A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your custom implementation to control error handling usually occurred during the poll operation before an Exchange have been created and being routed in Camel.

PollingConsumerPollStrategy

lazyStartProducer (producer (advanced))

Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel’s routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing.

false

boolean

backoffErrorThreshold (scheduler)

The number of subsequent error polls (failed due some error) that should happen before the backoffMultipler should kick-in.

int

backoffIdleThreshold (scheduler)

The number of subsequent idle polls that should happen before the backoffMultipler should kick-in.

int

backoffMultiplier (scheduler)

To let the scheduled polling consumer backoff if there has been a number of subsequent idles/errors in a row. The multiplier is then the number of polls that will be skipped before the next actual attempt is happening again. When this option is in use then backoffIdleThreshold and/or backoffErrorThreshold must also be configured.

int

delay (scheduler)

Milliseconds before the next poll.

500

long

greedy (scheduler)

If greedy is enabled, then the ScheduledPollConsumer will run immediately again, if the previous run polled 1 or more messages.

false

boolean

initialDelay (scheduler)

Milliseconds before the first poll starts.

1000

long

repeatCount (scheduler)

Specifies a maximum limit of number of fires. So if you set it to 1, the scheduler will only fire once. If you set it to 5, it will only fire five times. A value of zero or negative means fire forever.

0

long

runLoggingLevel (scheduler)

The consumer logs a start/complete log line when it polls. This option allows you to configure the logging level for that.

Enum values:

  • TRACE

  • DEBUG

  • INFO

  • WARN

  • ERROR

  • OFF

TRACE

LoggingLevel

scheduledExecutorService (scheduler)

Allows for configuring a custom/shared thread pool to use for the consumer. By default each consumer has its own single threaded thread pool.

ScheduledExecutorService

scheduler (scheduler)

To use a cron scheduler from either camel-spring or camel-quartz component. Use value spring or quartz for built in scheduler.

none

Object

schedulerProperties (scheduler)

To configure additional properties when using a custom scheduler or any of the Quartz, Spring based scheduler. This is a multi-value option with prefix: scheduler.

Map

startScheduler (scheduler)

Whether the scheduler should be auto started.

true

boolean

timeUnit (scheduler)

Time unit for initialDelay and delay options.

Enum values:

  • NANOSECONDS

  • MICROSECONDS

  • MILLISECONDS

  • SECONDS

  • MINUTES

  • HOURS

  • DAYS

MILLISECONDS

TimeUnit

useFixedDelay (scheduler)

Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in JDK for details.

true

boolean

Producer Operations

The Keycloak producer supports administrative operations on Keycloak instances via the Admin API.

Configuration

The Keycloak component supports four authentication methods:

  1. Access Token (Bearer Token) - Use a pre-obtained access token

  2. Refresh Token - Maintain long-running sessions with automatic token refresh

  3. Username/Password - Resource Owner Password Credentials flow

  4. Client Credentials - Service-to-service authentication

Access Token Authentication

Use this when you have a pre-obtained access token from an external authentication system:

  • Java

// Configure Keycloak component with access token
KeycloakComponent keycloak = context.getComponent("keycloak", KeycloakComponent.class);
KeycloakConfiguration config = new KeycloakConfiguration();
config.setServerUrl("http://localhost:8080");
config.setRealm("master");
config.setAccessToken("eyJhbGciOiJSUzI1NiIsInR5cC...");
keycloak.setConfiguration(config);

Refresh Token Authentication

Use this for long-running sessions that need to maintain authentication without storing credentials. The refresh token will be used to automatically obtain new access tokens when needed:

  • Java

// Configure Keycloak component with refresh token
KeycloakComponent keycloak = context.getComponent("keycloak", KeycloakComponent.class);
KeycloakConfiguration config = new KeycloakConfiguration();
config.setServerUrl("http://localhost:8080");
config.setRealm("master");
config.setClientId("my-client");
config.setRefreshToken("eyJhbGciOiJIUzI1NiIsInR5cCIgOi...");
// Optional: set client secret for confidential clients
config.setClientSecret("my-client-secret");
keycloak.setConfiguration(config);
Refresh token authentication requires a clientId. The clientSecret is optional and should be provided only if your client is configured as confidential in Keycloak.

Username/Password Authentication

Use this for admin user authentication:

  • Java

// Configure Keycloak component
KeycloakComponent keycloak = context.getComponent("keycloak", KeycloakComponent.class);
KeycloakConfiguration config = new KeycloakConfiguration();
config.setServerUrl("http://localhost:8080");
config.setRealm("master");
config.setUsername("admin");
config.setPassword("admin");
keycloak.setConfiguration(config);

Client Credentials Authentication

Use this for service-to-service authentication:

  • Java

// Configure Keycloak component with client credentials
KeycloakComponent keycloak = context.getComponent("keycloak", KeycloakComponent.class);
KeycloakConfiguration config = new KeycloakConfiguration();
config.setServerUrl("http://localhost:8080");
config.setRealm("master");
config.setClientId("my-service-client");
config.setClientSecret("my-client-secret");
keycloak.setConfiguration(config);

Supported Operations

The component supports the following operations:

  • Realm Management: createRealm, getRealm, updateRealm, deleteRealm

  • User Management: createUser, getUser, updateUser, listUsers, searchUsers, deleteUser

  • User Attributes: getUserAttributes, setUserAttribute, deleteUserAttribute

  • User Credentials: getUserCredentials, deleteUserCredential

  • User Actions: sendVerifyEmail, sendPasswordResetEmail, addRequiredAction, removeRequiredAction, executeActionsEmail

  • Role Management: createRole, getRole, updateRole, listRoles, deleteRole, assignRoleToUser, removeRoleFromUser, getUserRoles

  • Group Management: createGroup, getGroup, updateGroup, listGroups, deleteGroup, addUserToGroup, removeUserFromGroup, listUserGroups

  • Client Management: createClient, getClient, updateClient, listClients, deleteClient

  • Client Secret Management: getClientSecret, regenerateClientSecret

  • Client Role Management: createClientRole, getClientRole, updateClientRole, listClientRoles, deleteClientRole, assignClientRoleToUser, removeClientRoleFromUser

  • Password Management: resetUserPassword

  • Session Management: listUserSessions, logoutUser

  • Client Scope Management: createClientScope, getClientScope, updateClientScope, listClientScopes, deleteClientScope

  • Identity Provider Management: createIdentityProvider, getIdentityProvider, updateIdentityProvider, listIdentityProviders, deleteIdentityProvider

  • Authorization Services: createResource, getResource, updateResource, listResources, deleteResource, createResourcePolicy, getResourcePolicy, updateResourcePolicy, listResourcePolicies, deleteResourcePolicy, createResourcePermission, getResourcePermission, updateResourcePermission, listResourcePermissions, deleteResourcePermission, evaluatePermission

Realm Operations

  • Java

// Create a new realm
template.sendBodyAndHeader("keycloak:admin?operation=createRealm", null,
    KeycloakConstants.REALM_NAME, "my-new-realm");

// Get realm information
template.sendBodyAndHeader("keycloak:admin?operation=getRealm", null,
    KeycloakConstants.REALM_NAME, "my-realm");

// Delete a realm
template.sendBodyAndHeader("keycloak:admin?operation=deleteRealm", null,
    KeycloakConstants.REALM_NAME, "my-old-realm");

User Operations

  • Java

// Create a new user
Map<String, Object> headers = new HashMap<>();
headers.put(KeycloakConstants.REALM_NAME, "my-realm");
headers.put(KeycloakConstants.USERNAME, "john.doe");
headers.put(KeycloakConstants.USER_EMAIL, "john.doe@example.com");
headers.put(KeycloakConstants.USER_FIRST_NAME, "John");
headers.put(KeycloakConstants.USER_LAST_NAME, "Doe");

template.sendBodyAndHeaders("keycloak:admin?operation=createUser", null, headers);

// Set user password
Map<String, Object> passwordHeaders = new HashMap<>();
passwordHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
passwordHeaders.put(KeycloakConstants.USERNAME, "john.doe");
passwordHeaders.put("CamelKeycloakUserPassword", "secure-password");
passwordHeaders.put("CamelKeycloakUserPasswordTemporary", false);

template.sendBodyAndHeaders("keycloak:admin?operation=setUserPassword", null, passwordHeaders);

// List all users in realm
template.sendBodyAndHeader("keycloak:admin?operation=listUsers", null,
    KeycloakConstants.REALM_NAME, "my-realm");

// Delete a user
Map<String, Object> deleteHeaders = new HashMap<>();
deleteHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
deleteHeaders.put(KeycloakConstants.USERNAME, "john.doe");

template.sendBodyAndHeaders("keycloak:admin?operation=deleteUser", null, deleteHeaders);

Role Operations

  • Java

  • YAML

// Create a new role
Map<String, Object> roleHeaders = new HashMap<>();
roleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
roleHeaders.put(KeycloakConstants.ROLE_NAME, "manager");
roleHeaders.put(KeycloakConstants.ROLE_DESCRIPTION, "Manager role with elevated privileges");

template.sendBodyAndHeaders("keycloak:admin?operation=createRole", null, roleHeaders);

// Get role information
Map<String, Object> getRoleHeaders = new HashMap<>();
getRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
getRoleHeaders.put(KeycloakConstants.ROLE_NAME, "manager");

template.sendBodyAndHeaders("keycloak:admin?operation=getRole", null, getRoleHeaders);

// Assign role to user
Map<String, Object> assignHeaders = new HashMap<>();
assignHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
assignHeaders.put(KeycloakConstants.USERNAME, "john.doe");
assignHeaders.put(KeycloakConstants.ROLE_NAME, "manager");

template.sendBodyAndHeaders("keycloak:admin?operation=assignRoleToUser", null, assignHeaders);

// Delete a role
Map<String, Object> deleteRoleHeaders = new HashMap<>();
deleteRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
deleteRoleHeaders.put(KeycloakConstants.ROLE_NAME, "old-role");

template.sendBodyAndHeaders("keycloak:admin?operation=deleteRole", null, deleteRoleHeaders);
# Create role route
- route:
    from:
      uri: direct:create-role
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakRoleName
            simple: "${body[roleName]}"
        - setHeader:
            name: CamelKeycloakRoleDescription
            simple: "${body[description]}"
        - to:
            uri: keycloak:admin?operation=createRole
        - log: "Created role: ${header.CamelKeycloakRoleName}"

# Get role route
- route:
    from:
      uri: direct:get-role
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakRoleName
            simple: "${body[roleName]}"
        - to:
            uri: keycloak:admin?operation=getRole
        - log: "Role info: ${body}"

# Assign role to user route
- route:
    from:
      uri: direct:assign-role
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUsername
            simple: "${body[username]}"
        - setHeader:
            name: CamelKeycloakRoleName
            simple: "${body[roleName]}"
        - to:
            uri: keycloak:admin?operation=assignRoleToUser
        - log: "Assigned role ${header.CamelKeycloakRoleName} to user ${header.CamelKeycloakUsername}"

# Delete role route
- route:
    from:
      uri: direct:delete-role
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakRoleName
            simple: "${body[roleName]}"
        - to:
            uri: keycloak:admin?operation=deleteRole
        - log: "Deleted role: ${header.CamelKeycloakRoleName}"

Client Operations

  • Java

  • YAML

// Create a new client
Map<String, Object> clientHeaders = new HashMap<>();
clientHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
clientHeaders.put("CamelKeycloakClientId", "my-service-client");
clientHeaders.put("CamelKeycloakClientSecretRequired", true);
clientHeaders.put("CamelKeycloakClientDirectAccessGrantsEnabled", true);

template.sendBodyAndHeaders("keycloak:admin?operation=createClient", null, clientHeaders);

// Get client information
Map<String, Object> getClientHeaders = new HashMap<>();
getClientHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
getClientHeaders.put("CamelKeycloakClientId", "my-service-client");

template.sendBodyAndHeaders("keycloak:admin?operation=getClient", null, getClientHeaders);

// Get client secret
Map<String, Object> secretHeaders = new HashMap<>();
secretHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
secretHeaders.put("CamelKeycloakClientId", "my-service-client");

String clientSecret = template.requestBodyAndHeaders("keycloak:admin?operation=getClientSecret",
    null, secretHeaders, String.class);

// Delete a client
Map<String, Object> deleteClientHeaders = new HashMap<>();
deleteClientHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
deleteClientHeaders.put("CamelKeycloakClientId", "old-client");

template.sendBodyAndHeaders("keycloak:admin?operation=deleteClient", null, deleteClientHeaders);
# Create client route
- route:
    from:
      uri: direct:create-client
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientId
            simple: "${body[clientId]}"
        - setHeader:
            name: CamelKeycloakClientSecretRequired
            constant: true
        - setHeader:
            name: CamelKeycloakClientDirectAccessGrantsEnabled
            constant: true
        - to:
            uri: keycloak:admin?operation=createClient
        - log: "Created client: ${header.CamelKeycloakClientId}"

# Get client route
- route:
    from:
      uri: direct:get-client
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientId
            simple: "${body[clientId]}"
        - to:
            uri: keycloak:admin?operation=getClient
        - log: "Client info: ${body}"

# Get client secret route
- route:
    from:
      uri: direct:get-client-secret
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientId
            simple: "${body[clientId]}"
        - to:
            uri: keycloak:admin?operation=getClientSecret
        - log: "Client secret retrieved for: ${header.CamelKeycloakClientId}"

# Delete client route
- route:
    from:
      uri: direct:delete-client
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientId
            simple: "${body[clientId]}"
        - to:
            uri: keycloak:admin?operation=deleteClient
        - log: "Deleted client: ${header.CamelKeycloakClientId}"

Group Operations

  • Java

  • YAML

// Create a new group
Map<String, Object> groupHeaders = new HashMap<>();
groupHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
groupHeaders.put(KeycloakConstants.GROUP_NAME, "developers");

template.sendBodyAndHeaders("keycloak:admin?operation=createGroup", null, groupHeaders);

// List all groups
template.sendBodyAndHeader("keycloak:admin?operation=listGroups", null,
    KeycloakConstants.REALM_NAME, "my-realm");

// Add user to group
Map<String, Object> addToGroupHeaders = new HashMap<>();
addToGroupHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
addToGroupHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
addToGroupHeaders.put(KeycloakConstants.GROUP_ID, "group-id-456");

template.sendBodyAndHeaders("keycloak:admin?operation=addUserToGroup", null, addToGroupHeaders);

// List user's groups
Map<String, Object> userGroupsHeaders = new HashMap<>();
userGroupsHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
userGroupsHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

template.sendBodyAndHeaders("keycloak:admin?operation=listUserGroups", null, userGroupsHeaders);

// Delete a group
Map<String, Object> deleteGroupHeaders = new HashMap<>();
deleteGroupHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
deleteGroupHeaders.put(KeycloakConstants.GROUP_ID, "group-id-456");

template.sendBodyAndHeaders("keycloak:admin?operation=deleteGroup", null, deleteGroupHeaders);
# Create group route
- route:
    from:
      uri: direct:create-group
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakGroupName
            simple: "${body[groupName]}"
        - to:
            uri: keycloak:admin?operation=createGroup
        - log: "Created group: ${header.CamelKeycloakGroupName}"

# List groups route
- route:
    from:
      uri: direct:list-groups
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - to:
            uri: keycloak:admin?operation=listGroups
        - log: "Groups: ${body}"

# Add user to group route
- route:
    from:
      uri: direct:add-user-to-group
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakGroupId
            simple: "${body[groupId]}"
        - to:
            uri: keycloak:admin?operation=addUserToGroup
        - log: "Added user ${header.CamelKeycloakUserId} to group"

# List user groups route
- route:
    from:
      uri: direct:list-user-groups
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=listUserGroups
        - log: "User groups: ${body}"

Password Management Operations

  • Java

  • YAML

// Reset user password
Map<String, Object> passwordHeaders = new HashMap<>();
passwordHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
passwordHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
passwordHeaders.put(KeycloakConstants.USER_PASSWORD, "newSecurePassword123!");
passwordHeaders.put(KeycloakConstants.PASSWORD_TEMPORARY, false); // User won't need to change password

template.sendBodyAndHeaders("keycloak:admin?operation=resetUserPassword", null, passwordHeaders);

// Reset with temporary password (user must change on first login)
Map<String, Object> tempPasswordHeaders = new HashMap<>();
tempPasswordHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
tempPasswordHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
tempPasswordHeaders.put(KeycloakConstants.USER_PASSWORD, "tempPassword123");
tempPasswordHeaders.put(KeycloakConstants.PASSWORD_TEMPORARY, true);

template.sendBodyAndHeaders("keycloak:admin?operation=resetUserPassword", null, tempPasswordHeaders);
# Reset user password route
- route:
    from:
      uri: direct:reset-password
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakUserPassword
            simple: "${body[password]}"
        - setHeader:
            name: CamelKeycloakPasswordTemporary
            simple: "${body[temporary]}"
        - to:
            uri: keycloak:admin?operation=resetUserPassword
        - log: "Password reset for user ${header.CamelKeycloakUserId}"

# Reset with temporary password
- route:
    from:
      uri: direct:reset-temp-password
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakUserPassword
            simple: "${body[password]}"
        - setHeader:
            name: CamelKeycloakPasswordTemporary
            constant: true
        - to:
            uri: keycloak:admin?operation=resetUserPassword
        - log: "Temporary password set for user ${header.CamelKeycloakUserId}"

User Search Operations

  • Java

  • YAML

// Search users by query
Map<String, Object> searchHeaders = new HashMap<>();
searchHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
searchHeaders.put(KeycloakConstants.SEARCH_QUERY, "john");

template.sendBodyAndHeaders("keycloak:admin?operation=searchUsers", null, searchHeaders);

// Search with pagination
Map<String, Object> paginatedSearchHeaders = new HashMap<>();
paginatedSearchHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
paginatedSearchHeaders.put(KeycloakConstants.SEARCH_QUERY, "doe");
paginatedSearchHeaders.put(KeycloakConstants.FIRST_RESULT, 0);
paginatedSearchHeaders.put(KeycloakConstants.MAX_RESULTS, 10);

template.sendBodyAndHeaders("keycloak:admin?operation=searchUsers", null, paginatedSearchHeaders);

// Get user roles
Map<String, Object> rolesHeaders = new HashMap<>();
rolesHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
rolesHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

List<RoleRepresentation> roles = template.requestBodyAndHeaders(
    "keycloak:admin?operation=getUserRoles", null, rolesHeaders, List.class);
# Search users route
- route:
    from:
      uri: direct:search-users
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakSearchQuery
            simple: "${body[query]}"
        - to:
            uri: keycloak:admin?operation=searchUsers
        - log: "Search results: ${body}"

# Search with pagination
- route:
    from:
      uri: direct:search-users-paginated
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakSearchQuery
            simple: "${body[query]}"
        - setHeader:
            name: CamelKeycloakFirstResult
            simple: "${body[offset]}"
        - setHeader:
            name: CamelKeycloakMaxResults
            simple: "${body[limit]}"
        - to:
            uri: keycloak:admin?operation=searchUsers
        - log: "Found ${body.size} users"

# Get user roles route
- route:
    from:
      uri: direct:get-user-roles
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=getUserRoles
        - log: "User roles: ${body}"

Client Role Operations

  • Java

  • YAML

// Create client role
Map<String, Object> clientRoleHeaders = new HashMap<>();
clientRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
clientRoleHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");
clientRoleHeaders.put(KeycloakConstants.ROLE_NAME, "service-admin");
clientRoleHeaders.put(KeycloakConstants.ROLE_DESCRIPTION, "Service administrator role");

template.sendBodyAndHeaders("keycloak:admin?operation=createClientRole", null, clientRoleHeaders);

// List client roles
Map<String, Object> listClientRolesHeaders = new HashMap<>();
listClientRolesHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
listClientRolesHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");

template.sendBodyAndHeaders("keycloak:admin?operation=listClientRoles", null, listClientRolesHeaders);

// Assign client role to user
Map<String, Object> assignClientRoleHeaders = new HashMap<>();
assignClientRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
assignClientRoleHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
assignClientRoleHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");
assignClientRoleHeaders.put(KeycloakConstants.ROLE_NAME, "service-admin");

template.sendBodyAndHeaders("keycloak:admin?operation=assignClientRoleToUser", null, assignClientRoleHeaders);

// Remove client role from user
Map<String, Object> removeClientRoleHeaders = new HashMap<>();
removeClientRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
removeClientRoleHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
removeClientRoleHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");
removeClientRoleHeaders.put(KeycloakConstants.ROLE_NAME, "service-admin");

template.sendBodyAndHeaders("keycloak:admin?operation=removeClientRoleFromUser", null, removeClientRoleHeaders);
# Create client role route
- route:
    from:
      uri: direct:create-client-role
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientUuid
            simple: "${body[clientUuid]}"
        - setHeader:
            name: CamelKeycloakRoleName
            simple: "${body[roleName]}"
        - setHeader:
            name: CamelKeycloakRoleDescription
            simple: "${body[description]}"
        - to:
            uri: keycloak:admin?operation=createClientRole
        - log: "Created client role: ${header.CamelKeycloakRoleName}"

# List client roles route
- route:
    from:
      uri: direct:list-client-roles
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientUuid
            simple: "${body[clientUuid]}"
        - to:
            uri: keycloak:admin?operation=listClientRoles
        - log: "Client roles: ${body}"

# Assign client role to user
- route:
    from:
      uri: direct:assign-client-role
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakClientUuid
            simple: "${body[clientUuid]}"
        - setHeader:
            name: CamelKeycloakRoleName
            simple: "${body[roleName]}"
        - to:
            uri: keycloak:admin?operation=assignClientRoleToUser
        - log: "Assigned client role to user"

Session Management Operations

  • Java

  • YAML

// List user sessions
Map<String, Object> sessionsHeaders = new HashMap<>();
sessionsHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
sessionsHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

List<UserSessionRepresentation> sessions = template.requestBodyAndHeaders(
    "keycloak:admin?operation=listUserSessions", null, sessionsHeaders, List.class);

// Logout user (invalidate all sessions)
Map<String, Object> logoutHeaders = new HashMap<>();
logoutHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
logoutHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

template.sendBodyAndHeaders("keycloak:admin?operation=logoutUser", null, logoutHeaders);
# List user sessions route
- route:
    from:
      uri: direct:list-user-sessions
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=listUserSessions
        - log: "User sessions: ${body}"

# Logout user route
- route:
    from:
      uri: direct:logout-user
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=logoutUser
        - log: "User logged out: ${header.CamelKeycloakUserId}"

Client Scope Operations

  • Java

  • YAML

// Create client scope
Map<String, Object> scopeHeaders = new HashMap<>();
scopeHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
scopeHeaders.put(KeycloakConstants.CLIENT_SCOPE_NAME, "custom-scope");

template.sendBodyAndHeaders("keycloak:admin?operation=createClientScope", null, scopeHeaders);

// List client scopes
template.sendBodyAndHeader("keycloak:admin?operation=listClientScopes", null,
    KeycloakConstants.REALM_NAME, "my-realm");

// Get client scope
Map<String, Object> getScopeHeaders = new HashMap<>();
getScopeHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
getScopeHeaders.put(KeycloakConstants.CLIENT_SCOPE_ID, "scope-id-123");

template.sendBodyAndHeaders("keycloak:admin?operation=getClientScope", null, getScopeHeaders);

// Delete client scope
Map<String, Object> deleteScopeHeaders = new HashMap<>();
deleteScopeHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
deleteScopeHeaders.put(KeycloakConstants.CLIENT_SCOPE_ID, "scope-id-123");

template.sendBodyAndHeaders("keycloak:admin?operation=deleteClientScope", null, deleteScopeHeaders);
# Create client scope route
- route:
    from:
      uri: direct:create-client-scope
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientScopeName
            simple: "${body[scopeName]}"
        - to:
            uri: keycloak:admin?operation=createClientScope
        - log: "Created client scope: ${header.CamelKeycloakClientScopeName}"

# List client scopes route
- route:
    from:
      uri: direct:list-client-scopes
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - to:
            uri: keycloak:admin?operation=listClientScopes
        - log: "Client scopes: ${body}"

# Get client scope route
- route:
    from:
      uri: direct:get-client-scope
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientScopeId
            simple: "${body[scopeId]}"
        - to:
            uri: keycloak:admin?operation=getClientScope
        - log: "Client scope: ${body}"

Identity Provider Operations

Identity providers allow you to configure external authentication systems like LDAP, Active Directory, Google, GitHub, and other SAML/OIDC providers.

// Create OIDC identity provider
Map<String, Object> idpHeaders = new HashMap<>();
idpHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");

IdentityProviderRepresentation idp = new IdentityProviderRepresentation();
idp.setAlias("google-idp");
idp.setProviderId("google");
idp.setEnabled(true);
idp.setDisplayName("Google Login");

// Configure provider-specific settings
Map<String, String> config = new HashMap<>();
config.put("clientId", "google-client-id");
config.put("clientSecret", "google-client-secret");
idp.setConfig(config);

template.sendBodyAndHeaders("keycloak:admin?operation=createIdentityProvider&pojoRequest=true",
    idp, idpHeaders);

// List all identity providers
template.sendBodyAndHeader("keycloak:admin?operation=listIdentityProviders", null,
    KeycloakConstants.REALM_NAME, "my-realm");

// Get specific identity provider
Map<String, Object> getIdpHeaders = new HashMap<>();
getIdpHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
getIdpHeaders.put(KeycloakConstants.IDP_ALIAS, "google-idp");

IdentityProviderRepresentation provider = template.requestBodyAndHeaders(
    "keycloak:admin?operation=getIdentityProvider", null, getIdpHeaders,
    IdentityProviderRepresentation.class);

// Delete identity provider
template.sendBodyAndHeaders("keycloak:admin?operation=deleteIdentityProvider", null, getIdpHeaders);

User Attribute Operations

User attributes allow you to store custom key-value pairs on user accounts for additional metadata or application-specific data.

  • Java

  • YAML

// Set user attribute
Map<String, Object> attrHeaders = new HashMap<>();
attrHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
attrHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
attrHeaders.put(KeycloakConstants.ATTRIBUTE_NAME, "department");
attrHeaders.put(KeycloakConstants.ATTRIBUTE_VALUE, "Engineering");

template.sendBodyAndHeaders("keycloak:admin?operation=setUserAttribute", null, attrHeaders);

// Get all user attributes
Map<String, Object> getUserAttrHeaders = new HashMap<>();
getUserAttrHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
getUserAttrHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

Map<String, List<String>> attributes = template.requestBodyAndHeaders(
    "keycloak:admin?operation=getUserAttributes", null, getUserAttrHeaders, Map.class);

// Delete user attribute
Map<String, Object> delAttrHeaders = new HashMap<>();
delAttrHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
delAttrHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
delAttrHeaders.put(KeycloakConstants.ATTRIBUTE_NAME, "department");

template.sendBodyAndHeaders("keycloak:admin?operation=deleteUserAttribute", null, delAttrHeaders);
# Set user attribute
- route:
    from:
      uri: direct:set-user-attribute
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakAttributeName
            simple: "${body[attributeName]}"
        - setHeader:
            name: CamelKeycloakAttributeValue
            simple: "${body[attributeValue]}"
        - to:
            uri: keycloak:admin?operation=setUserAttribute
        - log: "Set attribute ${header.CamelKeycloakAttributeName}"

# Get user attributes
- route:
    from:
      uri: direct:get-user-attributes
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=getUserAttributes
        - log: "User attributes: ${body}"

# Delete user attribute
- route:
    from:
      uri: direct:delete-user-attribute
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakAttributeName
            simple: "${body[attributeName]}"
        - to:
            uri: keycloak:admin?operation=deleteUserAttribute
        - log: "Deleted attribute ${header.CamelKeycloakAttributeName}"

User Credential and Action Operations

Manage user credentials and trigger user actions such as email verification and password resets.

  • Java

  • YAML

// Get user credentials
Map<String, Object> credHeaders = new HashMap<>();
credHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
credHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

List<CredentialRepresentation> credentials = template.requestBodyAndHeaders(
    "keycloak:admin?operation=getUserCredentials", null, credHeaders, List.class);

// Delete specific credential
Map<String, Object> delCredHeaders = new HashMap<>();
delCredHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
delCredHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
delCredHeaders.put(KeycloakConstants.CREDENTIAL_ID, "credential-id-456");

template.sendBodyAndHeaders("keycloak:admin?operation=deleteUserCredential", null, delCredHeaders);

// Send verification email
Map<String, Object> verifyHeaders = new HashMap<>();
verifyHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
verifyHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

template.sendBodyAndHeaders("keycloak:admin?operation=sendVerifyEmail", null, verifyHeaders);

// Send password reset email
Map<String, Object> resetHeaders = new HashMap<>();
resetHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
resetHeaders.put(KeycloakConstants.USER_ID, "user-id-123");

template.sendBodyAndHeaders("keycloak:admin?operation=sendPasswordResetEmail", null, resetHeaders);

// Add required action
Map<String, Object> actionHeaders = new HashMap<>();
actionHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
actionHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
actionHeaders.put(KeycloakConstants.REQUIRED_ACTION, "VERIFY_EMAIL");

template.sendBodyAndHeaders("keycloak:admin?operation=addRequiredAction", null, actionHeaders);

// Execute multiple actions via email
Map<String, Object> execHeaders = new HashMap<>();
execHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
execHeaders.put(KeycloakConstants.USER_ID, "user-id-123");
execHeaders.put(KeycloakConstants.ACTIONS, Arrays.asList("UPDATE_PASSWORD", "VERIFY_EMAIL"));
execHeaders.put(KeycloakConstants.REDIRECT_URI, "https://myapp.com/auth/callback");
execHeaders.put(KeycloakConstants.LIFESPAN, 3600); // 1 hour

template.sendBodyAndHeaders("keycloak:admin?operation=executeActionsEmail", null, execHeaders);
# Get user credentials
- route:
    from:
      uri: direct:get-user-credentials
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=getUserCredentials
        - log: "User credentials: ${body}"

# Send verification email
- route:
    from:
      uri: direct:send-verify-email
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=sendVerifyEmail
        - log: "Verification email sent to user ${header.CamelKeycloakUserId}"

# Send password reset email
- route:
    from:
      uri: direct:send-password-reset
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - to:
            uri: keycloak:admin?operation=sendPasswordResetEmail
        - log: "Password reset email sent"

# Add required action
- route:
    from:
      uri: direct:add-required-action
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakRequiredAction
            constant: "VERIFY_EMAIL"
        - to:
            uri: keycloak:admin?operation=addRequiredAction
        - log: "Added required action VERIFY_EMAIL"

# Execute actions email
- route:
    from:
      uri: direct:execute-actions-email
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUserId
            simple: "${body[userId]}"
        - setHeader:
            name: CamelKeycloakActions
            constant:
              - "UPDATE_PASSWORD"
              - "VERIFY_EMAIL"
        - setHeader:
            name: CamelKeycloakRedirectUri
            constant: "https://myapp.com/auth/callback"
        - setHeader:
            name: CamelKeycloakLifespan
            constant: 3600
        - to:
            uri: keycloak:admin?operation=executeActionsEmail
        - log: "Sent actions email to user"

Client Secret Management

Retrieve and rotate client secrets for confidential clients.

  • Java

  • YAML

// Get client secret
Map<String, Object> getSecretHeaders = new HashMap<>();
getSecretHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
getSecretHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");

CredentialRepresentation secret = template.requestBodyAndHeaders(
    "keycloak:admin?operation=getClientSecret", null, getSecretHeaders,
    CredentialRepresentation.class);

System.out.println("Client secret: " + secret.getValue());

// Regenerate client secret (rotates the secret)
Map<String, Object> regenHeaders = new HashMap<>();
regenHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
regenHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");

CredentialRepresentation newSecret = template.requestBodyAndHeaders(
    "keycloak:admin?operation=regenerateClientSecret", null, regenHeaders,
    CredentialRepresentation.class);

System.out.println("New client secret: " + newSecret.getValue());
# Get client secret
- route:
    from:
      uri: direct:get-client-secret
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientUuid
            simple: "${body[clientUuid]}"
        - to:
            uri: keycloak:admin?operation=getClientSecret
        - log: "Retrieved client secret"

# Regenerate client secret
- route:
    from:
      uri: direct:regenerate-secret
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakClientUuid
            simple: "${body[clientUuid]}"
        - to:
            uri: keycloak:admin?operation=regenerateClientSecret
        - log: "Regenerated client secret: ${body.value}"
        - to: "direct:notify-secret-rotation"

Authorization Services Operations

Keycloak Authorization Services provide fine-grained authorization with resources, policies, and permissions.

These operations require a client with authorization services enabled in Keycloak.
// Create authorization resource
ResourceRepresentation resource = new ResourceRepresentation();
resource.setName("documents");
resource.setType("urn:myapp:resources:document");
resource.setUris(Collections.singleton("/documents/*"));
resource.addScope("read", "write", "delete");

Map<String, Object> resourceHeaders = new HashMap<>();
resourceHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
resourceHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");

Response response = template.requestBodyAndHeaders(
    "keycloak:admin?operation=createResource&pojoRequest=true",
    resource, resourceHeaders, Response.class);

// List all resources
template.sendBodyAndHeaders("keycloak:admin?operation=listResources", null, resourceHeaders);

// Create role-based policy
PolicyRepresentation policy = new PolicyRepresentation();
policy.setName("admin-policy");
policy.setType("role");
policy.setDescription("Only admins can access");

Map<String, Object> policyConfig = new HashMap<>();
policyConfig.put("roles", "[{\"id\":\"admin-role-id\",\"required\":true}]");
policy.setConfig(policyConfig);

Map<String, Object> policyHeaders = new HashMap<>();
policyHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
policyHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");

template.sendBodyAndHeaders("keycloak:admin?operation=createResourcePolicy&pojoRequest=true",
    policy, policyHeaders);

// Create resource permission
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
permission.setName("document-permission");
permission.addResource("documents");
permission.addPolicy("admin-policy");

Map<String, Object> permHeaders = new HashMap<>();
permHeaders.put(KeycloakConstants.REALM_NAME, "my-realm");
permHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123");

template.sendBodyAndHeaders("keycloak:admin?operation=createResourcePermission&pojoRequest=true",
    permission, permHeaders);

// List all permissions
template.sendBodyAndHeaders("keycloak:admin?operation=listResourcePermissions", null, permHeaders);

Complete Producer Example

  • Java

  • YAML

public class KeycloakManagementRoutes extends RouteBuilder {

    @Override
    public void configure() throws Exception {

        // Configure Keycloak component
        KeycloakComponent keycloak = getContext().getComponent("keycloak", KeycloakComponent.class);
        KeycloakConfiguration config = new KeycloakConfiguration();
        config.setServerUrl("http://localhost:8080");
        config.setRealm("master");
        config.setUsername("admin");
        config.setPassword("admin");
        keycloak.setConfiguration(config);

        // Comprehensive user management route
        from("direct:setup-user-environment")
            .routeId("setup-user-environment")
            .log("Setting up user environment...")

            // Step 1: Create realm
            .setHeader(KeycloakConstants.REALM_NAME, constant("my-company"))
            .to("keycloak:admin?operation=createRealm")
            .log("Created realm: my-company")

            // Step 2: Create roles
            .setHeader(KeycloakConstants.ROLE_NAME, constant("admin"))
            .setHeader(KeycloakConstants.ROLE_DESCRIPTION, constant("Administrator role"))
            .to("keycloak:admin?operation=createRole")
            .log("Created admin role")

            .setHeader(KeycloakConstants.ROLE_NAME, constant("user"))
            .setHeader(KeycloakConstants.ROLE_DESCRIPTION, constant("Standard user role"))
            .to("keycloak:admin?operation=createRole")
            .log("Created user role")

            // Step 3: Create client
            .setHeader("CamelKeycloakClientId", constant("my-app"))
            .setHeader("CamelKeycloakClientSecretRequired", constant(true))
            .setHeader("CamelKeycloakClientDirectAccessGrantsEnabled", constant(true))
            .to("keycloak:admin?operation=createClient")
            .log("Created client: my-app")

            // Step 4: Create users
            .setHeader(KeycloakConstants.USERNAME, constant("admin.user"))
            .setHeader(KeycloakConstants.USER_EMAIL, constant("admin@company.com"))
            .setHeader(KeycloakConstants.USER_FIRST_NAME, constant("Admin"))
            .setHeader(KeycloakConstants.USER_LAST_NAME, constant("User"))
            .to("keycloak:admin?operation=createUser")
            .log("Created admin user")

            // Step 5: Set password
            .setHeader("CamelKeycloakUserPassword", constant("admin123"))
            .setHeader("CamelKeycloakUserPasswordTemporary", constant(false))
            .to("keycloak:admin?operation=setUserPassword")
            .log("Set admin user password")

            // Step 6: Assign role
            .setHeader(KeycloakConstants.ROLE_NAME, constant("admin"))
            .to("keycloak:admin?operation=assignRoleToUser")
            .log("Assigned admin role to user")

            .transform().constant("User environment setup completed successfully");

        // User management API routes
        from("rest:post:/users")
            .routeId("create-user-api")
            .log("Creating user: ${body}")
            .setHeader(KeycloakConstants.REALM_NAME, constant("my-company"))
            .setHeader(KeycloakConstants.USERNAME, jsonpath("$.username"))
            .setHeader(KeycloakConstants.USER_EMAIL, jsonpath("$.email"))
            .setHeader(KeycloakConstants.USER_FIRST_NAME, jsonpath("$.firstName"))
            .setHeader(KeycloakConstants.USER_LAST_NAME, jsonpath("$.lastName"))
            .to("keycloak:admin?operation=createUser")
            .setHeader("Content-Type", constant("application/json"))
            .transform().constant("{\"status\": \"success\", \"message\": \"User created\"}");

        from("rest:get:/users")
            .routeId("list-users-api")
            .log("Listing users")
            .setHeader(KeycloakConstants.REALM_NAME, constant("my-company"))
            .to("keycloak:admin?operation=listUsers")
            .setHeader("Content-Type", constant("application/json"));

        from("rest:delete:/users/{username}")
            .routeId("delete-user-api")
            .log("Deleting user: ${header.username}")
            .setHeader(KeycloakConstants.REALM_NAME, constant("my-company"))
            .setHeader(KeycloakConstants.USERNAME, header("username"))
            .to("keycloak:admin?operation=deleteUser")
            .setHeader("Content-Type", constant("application/json"))
            .transform().constant("{\"status\": \"success\", \"message\": \"User deleted\"}");
    }
}
# Complete Keycloak producer configuration
- route:
    id: setup-user-environment
    from:
      uri: direct:setup-user-environment
      steps:
        - log: "Setting up user environment..."

        # Step 1: Create realm
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-company"
        - to:
            uri: keycloak:admin?operation=createRealm
        - log: "Created realm: my-company"

        # Step 2: Create admin role
        - setHeader:
            name: CamelKeycloakRoleName
            constant: "admin"
        - setHeader:
            name: CamelKeycloakRoleDescription
            constant: "Administrator role"
        - to:
            uri: keycloak:admin?operation=createRole
        - log: "Created admin role"

        # Step 3: Create user role
        - setHeader:
            name: CamelKeycloakRoleName
            constant: "user"
        - setHeader:
            name: CamelKeycloakRoleDescription
            constant: "Standard user role"
        - to:
            uri: keycloak:admin?operation=createRole
        - log: "Created user role"

        # Step 4: Create client
        - setHeader:
            name: CamelKeycloakClientId
            constant: "my-app"
        - setHeader:
            name: CamelKeycloakClientSecretRequired
            constant: true
        - setHeader:
            name: CamelKeycloakClientDirectAccessGrantsEnabled
            constant: true
        - to:
            uri: keycloak:admin?operation=createClient
        - log: "Created client: my-app"

        # Step 5: Create admin user
        - setHeader:
            name: CamelKeycloakUsername
            constant: "admin.user"
        - setHeader:
            name: CamelKeycloakUserEmail
            constant: "admin@company.com"
        - setHeader:
            name: CamelKeycloakUserFirstName
            constant: "Admin"
        - setHeader:
            name: CamelKeycloakUserLastName
            constant: "User"
        - to:
            uri: keycloak:admin?operation=createUser
        - log: "Created admin user"

        # Step 6: Set password
        - setHeader:
            name: CamelKeycloakUserPassword
            constant: "admin123"
        - setHeader:
            name: CamelKeycloakUserPasswordTemporary
            constant: false
        - to:
            uri: keycloak:admin?operation=setUserPassword
        - log: "Set admin user password"

        # Step 7: Assign role
        - setHeader:
            name: CamelKeycloakRoleName
            constant: "admin"
        - to:
            uri: keycloak:admin?operation=assignRoleToUser
        - log: "Assigned admin role to user"

        - transform:
            constant: "User environment setup completed successfully"

# REST API routes for user management
- rest:
    path: /users
    post:
      - to: direct:create-user-api

- rest:
    path: /users
    get:
      - to: direct:list-users-api

- rest:
    path: /users/{username}
    delete:
      - to: direct:delete-user-api

# Route implementations
- route:
    id: create-user-api
    from:
      uri: direct:create-user-api
      steps:
        - log: "Creating user: ${body}"
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-company"
        - setHeader:
            name: CamelKeycloakUsername
            jsonpath: "$.username"
        - setHeader:
            name: CamelKeycloakUserEmail
            jsonpath: "$.email"
        - setHeader:
            name: CamelKeycloakUserFirstName
            jsonpath: "$.firstName"
        - setHeader:
            name: CamelKeycloakUserLastName
            jsonpath: "$.lastName"
        - to:
            uri: keycloak:admin?operation=createUser
        - setHeader:
            name: Content-Type
            constant: "application/json"
        - transform:
            constant: '{"status": "success", "message": "User created"}'

- route:
    id: list-users-api
    from:
      uri: direct:list-users-api
      steps:
        - log: "Listing users"
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-company"
        - to:
            uri: keycloak:admin?operation=listUsers
        - setHeader:
            name: Content-Type
            constant: "application/json"

- route:
    id: delete-user-api
    from:
      uri: direct:delete-user-api
      steps:
        - log: "Deleting user: ${header.username}"
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-company"
        - setHeader:
            name: CamelKeycloakUsername
            header: "username"
        - to:
            uri: keycloak:admin?operation=deleteUser
        - setHeader:
            name: Content-Type
            constant: "application/json"
        - transform:
            constant: '{"status": "success", "message": "User deleted"}'

# Component configuration
camel:
  component:
    keycloak:
      server-url: "http://localhost:8080"
      realm: "master"
      username: "admin"
      password: "admin"

Consumer Operations

The Keycloak consumer allows you to poll and consume events from a Keycloak instance. This is useful for monitoring user activities, admin actions, and implementing event-driven workflows based on Keycloak events.

Overview

The consumer supports two types of events:

  • User Events (events) - Login attempts, logout events, register events, etc.

  • Admin Events (admin-events) - User created, role assigned, realm updated, etc.

The consumer uses a polling mechanism with fingerprint-based deduplication to ensure events are not processed multiple times.

Configuration

The consumer supports the same authentication methods as the producer (access token, refresh token, username/password, or client credentials):

  • Java (Access Token)

  • Java (Username/Password)

  • YAML

// Configure Keycloak component for consuming with access token
KeycloakComponent keycloak = context.getComponent("keycloak", KeycloakComponent.class);
KeycloakConfiguration config = new KeycloakConfiguration();
config.setServerUrl("http://localhost:8080");
config.setRealm("master"); // Auth realm
config.setAccessToken("eyJhbGciOiJSUzI1NiIsInR5cC...");
keycloak.setConfiguration(config);
// Configure Keycloak component for consuming with username/password
KeycloakComponent keycloak = context.getComponent("keycloak", KeycloakComponent.class);
KeycloakConfiguration config = new KeycloakConfiguration();
config.setServerUrl("http://localhost:8080");
config.setRealm("master"); // Auth realm
config.setUsername("admin");
config.setPassword("admin");
keycloak.setConfiguration(config);
# Configuration in application.yaml (using access token)
camel:
  component:
    keycloak:
      server-url: "http://localhost:8080"
      realm: "master"
      access-token: "eyJhbGciOiJSUzI1NiIsInR5cC..."

# Or using username/password
camel:
  component:
    keycloak:
      server-url: "http://localhost:8080"
      realm: "master"
      username: "admin"
      password: "admin"

Consuming Admin Events

Admin events are generated when administrative operations are performed in Keycloak, such as creating users, assigning roles, or updating realm settings.

  • Java

  • YAML

// Consume admin events from a specific realm
from("keycloak:adminEvents"
     + "?realm=my-realm"
     + "&eventType=admin-events"
     + "&maxResults=50"
     + "&initialDelay=1000"
     + "&delay=5000")
    .log("Received admin event: ${body}")
    .choice()
        .when(simple("${body.operationType} == 'CREATE'"))
            .log("Resource created: ${body.resourceType} at ${body.resourcePath}")
        .when(simple("${body.operationType} == 'UPDATE'"))
            .log("Resource updated: ${body.resourceType}")
        .when(simple("${body.operationType} == 'DELETE'"))
            .log("Resource deleted: ${body.resourceType}")
        .otherwise()
            .log("Other operation: ${body.operationType}")
    .end()
    .to("direct:process-admin-event");

// Process specific admin events
from("direct:process-admin-event")
    .process(exchange -> {
        AdminEventRepresentation event = exchange.getIn().getBody(AdminEventRepresentation.class);

        // Access event details
        String operationType = event.getOperationType();
        String resourceType = event.getResourceType();
        String resourcePath = event.getResourcePath();
        long timestamp = event.getTime();

        // Get auth details
        if (event.getAuthDetails() != null) {
            String userId = event.getAuthDetails().getUserId();
            String realmId = event.getAuthDetails().getRealmId();
            log.info("Operation performed by user: {} in realm: {}", userId, realmId);
        }

        // Process the event
        log.info("Processing {} operation on {} at {}", operationType, resourceType, timestamp);
    });
# Consume admin events
- route:
    id: consume-admin-events
    from:
      uri: >
        keycloak:adminEvents?
        realm=my-realm&
        eventType=admin-events&
        maxResults=50&
        initialDelay=1000&
        delay=5000
      steps:
        - log: "Received admin event: ${body}"
        - choice:
            when:
              - simple: "${body.operationType} == 'CREATE'"
                steps:
                  - log: "Resource created: ${body.resourceType} at ${body.resourcePath}"
              - simple: "${body.operationType} == 'UPDATE'"
                steps:
                  - log: "Resource updated: ${body.resourceType}"
              - simple: "${body.operationType} == 'DELETE'"
                steps:
                  - log: "Resource deleted: ${body.resourceType}"
            otherwise:
              steps:
                - log: "Other operation: ${body.operationType}"
        - to: "direct:process-admin-event"

# Process admin events
- route:
    id: process-admin-events
    from:
      uri: direct:process-admin-event
      steps:
        - log: "Processing admin event: ${body.operationType} on ${body.resourceType}"
        - to: "bean:auditService?method=recordAdminEvent"

Consuming User Events

User events track user activities such as logins, logouts, registration, password changes, and more.

  • Java

  • YAML

// Consume user events from a specific realm
from("keycloak:userEvents"
     + "?realm=my-realm"
     + "&eventType=events"
     + "&maxResults=50"
     + "&initialDelay=1000"
     + "&delay=5000")
    .log("Received user event: ${body}")
    .choice()
        .when(simple("${body.type} == 'LOGIN'"))
            .log("User logged in: ${body.userId} from IP ${body.ipAddress}")
            .to("direct:handle-login")
        .when(simple("${body.type} == 'LOGIN_ERROR'"))
            .log("Failed login attempt: ${body.userId}")
            .to("direct:handle-failed-login")
        .when(simple("${body.type} == 'LOGOUT'"))
            .log("User logged out: ${body.userId}")
            .to("direct:handle-logout")
        .when(simple("${body.type} == 'REGISTER'"))
            .log("New user registered: ${body.userId}")
            .to("direct:handle-registration")
        .otherwise()
            .log("Other event: ${body.type}")
    .end();

// Handle login events
from("direct:handle-login")
    .process(exchange -> {
        EventRepresentation event = exchange.getIn().getBody(EventRepresentation.class);

        String userId = event.getUserId();
        String ipAddress = event.getIpAddress();
        long timestamp = event.getTime();

        // Access event details
        if (event.getDetails() != null) {
            String username = event.getDetails().get("username");
            log.info("User {} logged in from {} at {}", username, ipAddress, timestamp);
        }
    })
    .to("bean:analyticsService?method=recordLogin");

// Handle failed login attempts
from("direct:handle-failed-login")
    .process(exchange -> {
        EventRepresentation event = exchange.getIn().getBody(EventRepresentation.class);
        String ipAddress = event.getIpAddress();

        // Check for suspicious activity
        log.warn("Failed login attempt from IP: {}", ipAddress);
    })
    .to("bean:securityService?method=checkFailedAttempts");
# Consume user events
- route:
    id: consume-user-events
    from:
      uri: >
        keycloak:userEvents?
        realm=my-realm&
        eventType=events&
        maxResults=50&
        initialDelay=1000&
        delay=5000
      steps:
        - log: "Received user event: ${body}"
        - choice:
            when:
              - simple: "${body.type} == 'LOGIN'"
                steps:
                  - log: "User logged in: ${body.userId} from IP ${body.ipAddress}"
                  - to: "direct:handle-login"
              - simple: "${body.type} == 'LOGIN_ERROR'"
                steps:
                  - log: "Failed login attempt: ${body.userId}"
                  - to: "direct:handle-failed-login"
              - simple: "${body.type} == 'LOGOUT'"
                steps:
                  - log: "User logged out: ${body.userId}"
                  - to: "direct:handle-logout"
              - simple: "${body.type} == 'REGISTER'"
                steps:
                  - log: "New user registered: ${body.userId}"
                  - to: "direct:handle-registration"
            otherwise:
              steps:
                - log: "Other event: ${body.type}"

# Handle login events
- route:
    id: handle-login
    from:
      uri: direct:handle-login
      steps:
        - log: "Processing login event for user ${body.userId}"
        - to: "bean:analyticsService?method=recordLogin"

# Handle failed login
- route:
    id: handle-failed-login
    from:
      uri: direct:handle-failed-login
      steps:
        - log: "Processing failed login from ${body.ipAddress}"
        - to: "bean:securityService?method=checkFailedAttempts"

Filtering Events

You can filter events using various options to narrow down the events you want to consume:

  • Java

  • YAML

// Filter admin events by operation type
from("keycloak:adminEvents"
     + "?realm=my-realm"
     + "&eventType=admin-events"
     + "&operationTypes=CREATE,UPDATE,DELETE"
     + "&maxResults=100")
    .log("Filtered admin event: ${body}");

// Filter user events by type
from("keycloak:userEvents"
     + "?realm=my-realm"
     + "&eventType=events"
     + "&types=LOGIN,LOGOUT,REGISTER"
     + "&maxResults=100")
    .log("Filtered user event: ${body}");

// Filter by date range
from("keycloak:adminEvents"
     + "?realm=my-realm"
     + "&eventType=admin-events"
     + "&dateFrom=1609459200000"  // milliseconds since epoch
     + "&dateTo=1640995200000"
     + "&maxResults=100")
    .log("Events in date range: ${body}");

// Filter by user and client
from("keycloak:userEvents"
     + "?realm=my-realm"
     + "&eventType=events"
     + "&user=user-id-123"
     + "&client=my-client-id"
     + "&ipAddress=192.168.1.100"
     + "&maxResults=50")
    .log("Specific user events: ${body}");
# Filter admin events by operation type
- route:
    from:
      uri: >
        keycloak:adminEvents?
        realm=my-realm&
        eventType=admin-events&
        operationTypes=CREATE,UPDATE,DELETE&
        maxResults=100
      steps:
        - log: "Filtered admin event: ${body}"

# Filter user events by type
- route:
    from:
      uri: >
        keycloak:userEvents?
        realm=my-realm&
        eventType=events&
        types=LOGIN,LOGOUT,REGISTER&
        maxResults=100
      steps:
        - log: "Filtered user event: ${body}"

# Filter by date range
- route:
    from:
      uri: >
        keycloak:adminEvents?
        realm=my-realm&
        eventType=admin-events&
        dateFrom=1609459200000&
        dateTo=1640995200000&
        maxResults=100
      steps:
        - log: "Events in date range: ${body}"

# Filter by user and client
- route:
    from:
      uri: >
        keycloak:userEvents?
        realm=my-realm&
        eventType=events&
        user=user-id-123&
        client=my-client-id&
        ipAddress=192.168.1.100&
        maxResults=50
      steps:
        - log: "Specific user events: ${body}"

Event Processing Patterns

Audit Trail

  • Java

  • YAML

// Create comprehensive audit trail from admin events
from("keycloak:adminEvents"
     + "?realm=my-realm"
     + "&eventType=admin-events"
     + "&maxResults=100"
     + "&delay=10000")
    .process(exchange -> {
        AdminEventRepresentation event = exchange.getIn().getBody(AdminEventRepresentation.class);

        // Build audit record
        Map<String, Object> auditRecord = new HashMap<>();
        auditRecord.put("timestamp", new Date(event.getTime()));
        auditRecord.put("operation", event.getOperationType());
        auditRecord.put("resourceType", event.getResourceType());
        auditRecord.put("resourcePath", event.getResourcePath());

        if (event.getAuthDetails() != null) {
            auditRecord.put("userId", event.getAuthDetails().getUserId());
            auditRecord.put("ipAddress", event.getAuthDetails().getIpAddress());
        }

        exchange.getIn().setBody(auditRecord);
    })
    .marshal().json()
    .to("kafka:audit-trail?brokers=localhost:9092")
    .to("jdbc:dataSource?useHeadersAsParameters=true");
# Audit trail pattern
- route:
    from:
      uri: >
        keycloak:adminEvents?
        realm=my-realm&
        eventType=admin-events&
        maxResults=100&
        delay=10000
      steps:
        - setBody:
            simple: >
              {
                "timestamp": ${body.time},
                "operation": "${body.operationType}",
                "resourceType": "${body.resourceType}",
                "resourcePath": "${body.resourcePath}",
                "userId": "${body.authDetails.userId}",
                "ipAddress": "${body.authDetails.ipAddress}"
              }
        - marshal:
            json: {}
        - to: "kafka:audit-trail?brokers=localhost:9092"
        - to: "jdbc:dataSource?useHeadersAsParameters=true"

Security Monitoring

  • Java

  • YAML

// Monitor for security-relevant events
from("keycloak:userEvents"
     + "?realm=my-realm"
     + "&eventType=events"
     + "&types=LOGIN_ERROR,UPDATE_PASSWORD,UPDATE_EMAIL"
     + "&maxResults=50"
     + "&delay=5000")
    .filter(simple("${body.type} == 'LOGIN_ERROR'"))
    .aggregate(simple("${body.ipAddress}"), new ArrayListAggregationStrategy())
        .completionSize(5)      // 5 failed attempts
        .completionTimeout(300000) // within 5 minutes
    .process(exchange -> {
        List<EventRepresentation> failedAttempts = exchange.getIn().getBody(List.class);
        String ipAddress = failedAttempts.get(0).getIpAddress();

        log.warn("SECURITY ALERT: {} failed login attempts from IP: {}",
                 failedAttempts.size(), ipAddress);
    })
    .to("direct:block-ip")
    .to("direct:send-security-alert");
# Security monitoring pattern
- route:
    from:
      uri: >
        keycloak:userEvents?
        realm=my-realm&
        eventType=events&
        types=LOGIN_ERROR,UPDATE_PASSWORD,UPDATE_EMAIL&
        maxResults=50&
        delay=5000
      steps:
        - filter:
            simple: "${body.type} == 'LOGIN_ERROR'"
        - aggregate:
            correlationExpression:
              simple: "${body.ipAddress}"
            aggregationStrategy: "#arrayListAggregation"
            completionSize: 5
            completionTimeout: 300000
            steps:
              - log: "SECURITY ALERT: Multiple failed login attempts from ${body[0].ipAddress}"
              - to: "direct:block-ip"
              - to: "direct:send-security-alert"

User Activity Analytics

  • Java

  • YAML

// Track user activity for analytics
from("keycloak:userEvents"
     + "?realm=my-realm"
     + "&eventType=events"
     + "&types=LOGIN,LOGOUT"
     + "&maxResults=100"
     + "&delay=60000")
    .process(exchange -> {
        EventRepresentation event = exchange.getIn().getBody(EventRepresentation.class);

        // Extract analytics data
        Map<String, Object> analytics = new HashMap<>();
        analytics.put("userId", event.getUserId());
        analytics.put("eventType", event.getType());
        analytics.put("timestamp", new Date(event.getTime()));
        analytics.put("ipAddress", event.getIpAddress());
        analytics.put("sessionId", event.getSessionId());

        if (event.getDetails() != null) {
            analytics.put("username", event.getDetails().get("username"));
            analytics.put("clientId", event.getDetails().get("client_id"));
        }

        exchange.getIn().setBody(analytics);
    })
    .to("bean:analyticsService?method=recordActivity")
    .to("elasticsearch://keycloak-events?operation=Index&indexName=user-activity");
# User activity analytics
- route:
    from:
      uri: >
        keycloak:userEvents?
        realm=my-realm&
        eventType=events&
        types=LOGIN,LOGOUT&
        maxResults=100&
        delay=60000
      steps:
        - setBody:
            simple: >
              {
                "userId": "${body.userId}",
                "eventType": "${body.type}",
                "timestamp": ${body.time},
                "ipAddress": "${body.ipAddress}",
                "sessionId": "${body.sessionId}"
              }
        - to: "bean:analyticsService?method=recordActivity"
        - to: "elasticsearch://keycloak-events?operation=Index&indexName=user-activity"

Consumer Options

The consumer supports the following configuration options:

Option Default Description

realm

The Keycloak realm to consume events from (required)

eventType

events

Type of events to consume: events or admin-events

maxResults

100

Maximum number of events to retrieve per poll

first

0

Offset for pagination (first result index)

initialDelay

1000

Delay before first poll (milliseconds)

delay

500

Delay between polls (milliseconds)

Common Filter Options

client

Filter events by client ID

user

Filter events by user ID

dateFrom

Filter events from this timestamp (milliseconds since epoch)

dateTo

Filter events until this timestamp (milliseconds since epoch)

ipAddress

Filter events by IP address

User Event Filters

types

Filter by event types (comma-separated, e.g., LOGIN,LOGOUT,REGISTER)

Admin Event Filters

operationTypes

Filter by operation types (comma-separated, e.g., CREATE,UPDATE,DELETE)

authRealmFilter

Filter by authentication realm

authClient

Filter by authentication client ID

authUser

Filter by authentication user ID

authIpAddress

Filter by authentication IP address

resourcePath

Filter by resource path

Exchange Headers

The consumer sets the following headers on the exchange:

Header Description

CamelKeycloakEventType

Type of event: event or admin-event

CamelKeycloakEventId

Event timestamp (milliseconds since epoch)

CamelKeycloakRealmName

Realm name where the event occurred

Message Body

The message body contains:

  • For user events: org.keycloak.representations.idm.EventRepresentation

  • For admin events: org.keycloak.representations.idm.AdminEventRepresentation

Complete Consumer Example

  • Java

  • YAML

public class KeycloakEventMonitoringRoutes extends RouteBuilder {

    @Override
    public void configure() throws Exception {

        // Configure Keycloak component
        KeycloakComponent keycloak = getContext().getComponent("keycloak", KeycloakComponent.class);
        KeycloakConfiguration config = new KeycloakConfiguration();
        config.setServerUrl("http://localhost:8080");
        config.setRealm("master");
        config.setUsername("admin");
        config.setPassword("admin");
        keycloak.setConfiguration(config);

        // Consume admin events and send to audit system
        from("keycloak:adminEvents"
             + "?realm=production-realm"
             + "&eventType=admin-events"
             + "&operationTypes=CREATE,UPDATE,DELETE"
             + "&maxResults=100"
             + "&delay=10000")
            .routeId("admin-events-audit")
            .log("Admin event: ${body.operationType} on ${body.resourceType}")
            .to("direct:audit-trail");

        // Consume user login events for analytics
        from("keycloak:userEvents"
             + "?realm=production-realm"
             + "&eventType=events"
             + "&types=LOGIN,LOGOUT"
             + "&maxResults=50"
             + "&delay=30000")
            .routeId("user-activity-tracking")
            .log("User activity: ${body.type} for user ${body.userId}")
            .to("direct:analytics");

        // Monitor failed logins for security
        from("keycloak:userEvents"
             + "?realm=production-realm"
             + "&eventType=events"
             + "&types=LOGIN_ERROR"
             + "&maxResults=100"
             + "&delay=5000")
            .routeId("security-monitoring")
            .log("Failed login from IP: ${body.ipAddress}")
            .to("direct:security-check");

        // Process audit trail
        from("direct:audit-trail")
            .marshal().json()
            .to("kafka:admin-audit?brokers=localhost:9092")
            .to("log:audit");

        // Process analytics
        from("direct:analytics")
            .to("bean:analyticsService?method=processUserActivity")
            .to("log:analytics");

        // Process security alerts
        from("direct:security-check")
            .to("bean:securityService?method=checkFailedLogin")
            .to("log:security");
    }
}
# Component configuration
camel:
  component:
    keycloak:
      server-url: "http://localhost:8080"
      realm: "master"
      username: "admin"
      password: "admin"

# Routes
- route:
    id: admin-events-audit
    from:
      uri: >
        keycloak:adminEvents?
        realm=production-realm&
        eventType=admin-events&
        operationTypes=CREATE,UPDATE,DELETE&
        maxResults=100&
        delay=10000
      steps:
        - log: "Admin event: ${body.operationType} on ${body.resourceType}"
        - to: "direct:audit-trail"

- route:
    id: user-activity-tracking
    from:
      uri: >
        keycloak:userEvents?
        realm=production-realm&
        eventType=events&
        types=LOGIN,LOGOUT&
        maxResults=50&
        delay=30000
      steps:
        - log: "User activity: ${body.type} for user ${body.userId}"
        - to: "direct:analytics"

- route:
    id: security-monitoring
    from:
      uri: >
        keycloak:userEvents?
        realm=production-realm&
        eventType=events&
        types=LOGIN_ERROR&
        maxResults=100&
        delay=5000
      steps:
        - log: "Failed login from IP: ${body.ipAddress}"
        - to: "direct:security-check"

# Processing routes
- route:
    id: process-audit-trail
    from:
      uri: direct:audit-trail
      steps:
        - marshal:
            json: {}
        - to: "kafka:admin-audit?brokers=localhost:9092"
        - to: "log:audit"

- route:
    id: process-analytics
    from:
      uri: direct:analytics
      steps:
        - to: "bean:analyticsService?method=processUserActivity"
        - to: "log:analytics"

- route:
    id: process-security-check
    from:
      uri: direct:security-check
      steps:
        - to: "bean:securityService?method=checkFailedLogin"
        - to: "log:security"

Enabling Events in Keycloak

Before consuming events, you must enable event logging in Keycloak:

Enable Admin Events

  1. Login to Keycloak Admin Console

  2. Select your realm

  3. Go to Realm SettingsEvents tab

  4. In Admin Events Settings section:

    • Toggle Save Events: ON

    • Toggle Include Representation: ON (optional, for detailed event data)

Enable User Events

  1. In the same Events tab

  2. In User Events Settings section:

    • Toggle Save Events: ON

    • Set Expiration (e.g., 30 days)

    • Add Event Listeners: jboss-logging (default)

    • Select Saved Types: Choose which event types to save (LOGIN, LOGOUT, etc.)

Event Deduplication

The consumer uses fingerprint-based deduplication to prevent processing the same event multiple times:

  • Events are uniquely identified by combining timestamp and event-specific properties

  • Fingerprints are cached per timestamp and cleared when moving to newer timestamps

  • Maximum cache size is 1000 fingerprints to prevent memory issues

  • This ensures reliable event processing even with high event volumes

Security Policies

The Keycloak security policy provides route-level authorization using Keycloak authentication and authorization services.

Features

The Keycloak security policy supports:

  • Role-based authorization - Validate user roles from Keycloak tokens

  • Permission-based authorization - Validate fine-grained permissions using Keycloak Authorization Services

  • Token validation - Verify access tokens from Keycloak

  • Flexible configuration - Support for client credentials and resource owner password flows

Configuration

Basic Setup

  • Java

  • YAML

KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy();
policy.setServerUrl("http://localhost:8080");
policy.setRealm("my-realm");
policy.setClientId("my-client");
policy.setClientSecret("my-client-secret");
- route:
    from:
      uri: direct:start
      steps:
        - policy:
            ref: keycloakPolicy
        - to:
            uri: mock:result

# Bean definition in beans configuration
beans:
  - name: keycloakPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "http://localhost:8080"
      realm: "my-realm"
      clientId: "my-client"
      clientSecret: "my-client-secret"

Role-based Authorization

  • Java

  • YAML

KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(
    "http://localhost:8080", "my-realm", "my-client", "client-secret");

// Require specific roles (comma-separated)
policy.setRequiredRoles("admin,user");
policy.setAllRolesRequired(true); // User must have ALL roles

from("direct:admin")
    .policy(policy)
    .to("mock:admin-endpoint");
- route:
    from:
      uri: direct:admin
      steps:
        - policy:
            ref: adminPolicy
        - to:
            uri: mock:admin-endpoint

# Bean definition
beans:
  - name: adminPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "http://localhost:8080"
      realm: "my-realm"
      clientId: "my-client"
      clientSecret: "client-secret"
      requiredRoles: "admin,user"
      allRolesRequired: true

Permission-based Authorization

  • Java

  • YAML

KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(
    "http://localhost:8080", "my-realm", "my-client", "client-secret");

// Require specific permissions (comma-separated)
policy.setRequiredPermissions("read:documents,write:documents");
policy.setAllPermissionsRequired(false); // User needs ANY permission

from("direct:documents")
    .policy(policy)
    .to("mock:documents-endpoint");
- route:
    from:
      uri: direct:documents
      steps:
        - policy:
            ref: documentsPolicy
        - to:
            uri: mock:documents-endpoint

# Bean definition
beans:
  - name: documentsPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "http://localhost:8080"
      realm: "my-realm"
      clientId: "my-client"
      clientSecret: "client-secret"
      requiredPermissions: "read:documents,write:documents"
      allPermissionsRequired: false

Resource Owner Password Credentials

  • Java

  • YAML

KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(
    "http://localhost:8080", "my-realm", "my-client", "username", "password");

from("direct:user-flow")
    .policy(policy)
    .to("mock:result");
- route:
    from:
      uri: direct:user-flow
      steps:
        - policy:
            ref: userFlowPolicy
        - to:
            uri: mock:result

# Bean definition
beans:
  - name: userFlowPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "http://localhost:8080"
      realm: "my-realm"
      clientId: "my-client"
      username: "username"
      password: "password"
      useResourceOwnerPasswordCredentials: true

Usage

Providing Access Tokens

The security policy expects access tokens to be provided in one of the following ways:

  1. Header: CamelKeycloakAccessToken

  2. Authorization Header: Authorization: Bearer <token>

  3. Exchange Property: CamelKeycloakAccessToken

// Using header
template.sendBodyAndHeader("direct:protected", "message",
    KeycloakSecurityConstants.ACCESS_TOKEN_HEADER, accessToken);

// Using Authorization header
template.sendBodyAndHeader("direct:protected", "message",
    "Authorization", "Bearer " + accessToken);

Route Examples

  • Java

  • YAML

from("direct:admin-only")
    .policy(adminPolicy)
    .transform().constant("Admin access granted")
    .to("mock:admin");

from("direct:user-or-admin")
    .policy(userPolicy)
    .transform().constant("User access granted")
    .to("mock:user");

from("rest:get:/api/documents")
    .policy(documentsPolicy)
    .to("direct:list-documents");
- route:
    from:
      uri: direct:admin-only
      steps:
        - policy:
            ref: adminPolicy
        - transform:
            constant: "Admin access granted"
        - to:
            uri: mock:admin

- route:
    from:
      uri: direct:user-or-admin
      steps:
        - policy:
            ref: userPolicy
        - transform:
            constant: "User access granted"
        - to:
            uri: mock:user

- rest:
    get:
      - uri: /api/documents
        to: direct:list-documents
        route:
          policy:
            ref: documentsPolicy

Configuration Options

Name Default Description

serverUrl

Keycloak server URL (e.g., http://localhost:8080)

realm

Keycloak realm name

clientId

Keycloak client ID

clientSecret

Keycloak client secret (for client credentials flow)

username

Username (for resource owner password flow)

password

Password (for resource owner password flow)

requiredRoles

""

Comma-separated list of required roles (e.g., "admin,user,manager")

requiredPermissions

""

Comma-separated list of required permissions (e.g., "read:documents,write:documents")

allRolesRequired

true

Whether ALL roles are required (true) or ANY role (false)

allPermissionsRequired

true

Whether ALL permissions are required (true) or ANY permission (false)

useResourceOwnerPasswordCredentials

false

Whether to use resource owner password flow

Security Considerations

  • Always use HTTPS in production environments

  • Store client secrets securely (environment variables, secret management systems)

  • Regularly rotate client secrets and user passwords

  • Use the principle of least privilege when assigning roles and permissions

  • Consider token expiration and refresh strategies

Error Handling

The component throws CamelAuthorizationException when:

  • Access token is missing or invalid

  • User doesn’t have required roles

  • User doesn’t have required permissions

  • Keycloak server is unreachable

  • Token verification fails

  • Java

  • YAML

onException(CamelAuthorizationException.class)
    .handled(true)
    .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
    .transform().constant("Access denied");
- onException:
    exception:
      - "org.apache.camel.CamelAuthorizationException"
    handled: true
    steps:
      - setHeader:
          name: "CamelHttpResponseCode"
          constant: 403
      - transform:
          constant: "Access denied"

Examples

Basic Role-based Authorization

  • Java

  • YAML

// Create Keycloak security policy
KeycloakSecurityPolicy keycloakPolicy = new KeycloakSecurityPolicy();
keycloakPolicy.setServerUrl("http://localhost:8080");
keycloakPolicy.setRealm("my-company");
keycloakPolicy.setClientId("my-service");
keycloakPolicy.setClientSecret("client-secret-value");

// Require admin role (comma-separated string)
keycloakPolicy.setRequiredRoles("admin");

// Apply to route
from("direct:admin-endpoint")
    .policy(keycloakPolicy)
    .transform().constant("Admin access granted")
    .to("mock:admin-result");
- route:
    from:
      uri: direct:admin-endpoint
      steps:
        - policy:
            ref: keycloakPolicy
        - transform:
            constant: "Admin access granted"
        - to:
            uri: mock:admin-result

# Bean definition
beans:
  - name: keycloakPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "http://localhost:8080"
      realm: "my-company"
      clientId: "my-service"
      clientSecret: "client-secret-value"
      requiredRoles: "admin"

Multiple Role Authorization

  • Java

  • YAML

// Require either admin OR user role (comma-separated string)
KeycloakSecurityPolicy userPolicy = new KeycloakSecurityPolicy(
    "http://localhost:8080", "my-company", "my-service", "client-secret");
userPolicy.setRequiredRoles("admin,user");
userPolicy.setAllRolesRequired(false); // ANY role (OR logic)

from("direct:user-endpoint")
    .policy(userPolicy)
    .to("bean:userService?method=processUser");
- route:
    from:
      uri: direct:user-endpoint
      steps:
        - policy:
            ref: userPolicy
        - to:
            uri: bean:userService?method=processUser

# Bean definition
beans:
  - name: userPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "http://localhost:8080"
      realm: "my-company"
      clientId: "my-service"
      clientSecret: "client-secret"
      requiredRoles: "admin,user"
      allRolesRequired: false

REST API with Keycloak Protection

  • Java

  • YAML

// Configure different policies for different endpoints
KeycloakSecurityPolicy readPolicy = new KeycloakSecurityPolicy(
    "{{keycloak.server-url}}", "{{keycloak.realm}}",
    "{{keycloak.client-id}}", "{{keycloak.client-secret}}");
readPolicy.setRequiredRoles("reader,writer,admin");
readPolicy.setAllRolesRequired(false);

KeycloakSecurityPolicy writePolicy = new KeycloakSecurityPolicy(
    "{{keycloak.server-url}}", "{{keycloak.realm}}",
    "{{keycloak.client-id}}", "{{keycloak.client-secret}}");
writePolicy.setRequiredRoles("writer,admin");
writePolicy.setAllRolesRequired(false);

KeycloakSecurityPolicy adminPolicy = new KeycloakSecurityPolicy(
    "{{keycloak.server-url}}", "{{keycloak.realm}}",
    "{{keycloak.client-id}}", "{{keycloak.client-secret}}");
adminPolicy.setRequiredRoles("admin");

// Configure REST endpoints
rest("/api")
    .get("/documents")
        .route()
        .policy(readPolicy)
        .to("bean:documentService?method=listDocuments")
        .endRest()
    .post("/documents")
        .route()
        .policy(writePolicy)
        .to("bean:documentService?method=createDocument")
        .endRest()
    .delete("/documents/{id}")
        .route()
        .policy(adminPolicy)
        .to("bean:documentService?method=deleteDocument")
        .endRest();
- rest:
    path: "/api"
    get:
      - uri: "/documents"
        to: bean:documentService?method=listDocuments
        route:
          policy:
            ref: readPolicy
    post:
      - uri: "/documents"
        to: bean:documentService?method=createDocument
        route:
          policy:
            ref: writePolicy
    delete:
      - uri: "/documents/{id}"
        to: bean:documentService?method=deleteDocument
        route:
          policy:
            ref: adminPolicy

# Bean definitions for policies
beans:
  - name: readPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "{{keycloak.server-url}}"
      realm: "{{keycloak.realm}}"
      clientId: "{{keycloak.client-id}}"
      clientSecret: "{{keycloak.client-secret}}"
      requiredRoles: "reader,writer,admin"
      allRolesRequired: false

  - name: writePolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "{{keycloak.server-url}}"
      realm: "{{keycloak.realm}}"
      clientId: "{{keycloak.client-id}}"
      clientSecret: "{{keycloak.client-secret}}"
      requiredRoles: "writer,admin"
      allRolesRequired: false

  - name: adminPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "{{keycloak.server-url}}"
      realm: "{{keycloak.realm}}"
      clientId: "{{keycloak.client-id}}"
      clientSecret: "{{keycloak.client-secret}}"
      requiredRoles: "admin"

Sending Requests with Tokens

// In your client code, include the access token
String accessToken = "eyJhbGciOiJSUzI1NiIsInR5cC..."; // From Keycloak

// Option 1: Using custom header
template.sendBodyAndHeader("direct:protected-endpoint",
    requestBody,
    KeycloakSecurityConstants.ACCESS_TOKEN_HEADER,
    accessToken);

// Option 2: Using Authorization header (standard)
template.sendBodyAndHeader("direct:protected-endpoint",
    requestBody,
    "Authorization",
    "Bearer " + accessToken);

// Option 3: Using exchange property
Exchange exchange = ExchangeBuilder.anExchange(camelContext)
    .withBody(requestBody)
    .withProperty(KeycloakSecurityConstants.ACCESS_TOKEN_PROPERTY, accessToken)
    .build();
template.send("direct:protected-endpoint", exchange);

Advanced Error Handling

  • Java

  • YAML

// Global error handler for authorization failures
onException(CamelAuthorizationException.class)
    .handled(true)
    .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
    .setHeader("Content-Type", constant("application/json"))
    .transform().constant("{\"error\": \"Access denied\", \"message\": \"Insufficient privileges\"}")
    .log("Authorization failed: ${exception.message}");

// Route-specific error handling
from("rest:post:/secure-data")
    .doTry()
        .policy(keycloakPolicy)
        .to("bean:dataProcessor")
    .doCatch(CamelAuthorizationException.class)
        .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
        .transform().constant("Access denied")
    .end();
# Global error handler
- onException:
    exception:
      - "org.apache.camel.CamelAuthorizationException"
    handled: true
    steps:
      - setHeader:
          name: "CamelHttpResponseCode"
          constant: 403
      - setHeader:
          name: "Content-Type"
          constant: "application/json"
      - transform:
          constant: '{"error": "Access denied", "message": "Insufficient privileges"}'
      - log: "Authorization failed: ${exception.message}"

# Route-specific error handling
- route:
    from:
      uri: rest:post:/secure-data
      steps:
        - doTry:
            steps:
              - policy:
                  ref: keycloakPolicy
              - to:
                  uri: bean:dataProcessor
            doCatch:
              - exception:
                  - "org.apache.camel.CamelAuthorizationException"
                steps:
                  - setHeader:
                      name: "CamelHttpResponseCode"
                      constant: 403
                  - transform:
                      constant: "Access denied"

Configuration Properties

Properties
# application.properties
keycloak.server-url=http://localhost:8080
keycloak.realm=my-company
keycloak.client-id=my-service
keycloak.client-secret=your-client-secret

=== Spring Configuration

@Configuration
public class SecurityConfiguration {

    @Value("${keycloak.server-url}")
    private String serverUrl;

    @Value("${keycloak.realm}")
    private String realm;

    @Value("${keycloak.client-id}")
    private String clientId;

    @Value("${keycloak.client-secret}")
    private String clientSecret;

    @Bean
    public KeycloakSecurityPolicy adminPolicy() {
        KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(
            serverUrl, realm, clientId, clientSecret);
        policy.setRequiredRoles("admin");
        return policy;
    }

    @Bean
    public KeycloakSecurityPolicy userPolicy() {
        KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(
            serverUrl, realm, clientId, clientSecret);
        policy.setRequiredRoles("user,admin");
        policy.setAllRolesRequired(false);
        return policy;
    }
}

=== Complete Route Configuration

Java
public class KeycloakSecurityRoutes extends RouteBuilder {

    @Override
    public void configure() throws Exception {

        // Admin policy - requires admin role
        KeycloakSecurityPolicy adminPolicy = new KeycloakSecurityPolicy(
            "{{keycloak.server-url}}", "{{keycloak.realm}}",
            "{{keycloak.client-id}}", "{{keycloak.client-secret}}");
        adminPolicy.setRequiredRoles("admin");

        // User policy - requires user or admin role
        KeycloakSecurityPolicy userPolicy = new KeycloakSecurityPolicy(
            "{{keycloak.server-url}}", "{{keycloak.realm}}",
            "{{keycloak.client-id}}", "{{keycloak.client-secret}}");
        userPolicy.setRequiredRoles("user,admin");
        userPolicy.setAllRolesRequired(false); // ANY role

        // Error handling
        onException(CamelAuthorizationException.class)
            .handled(true)
            .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
            .transform().constant("Forbidden");

        // Routes
        from("rest:get:/admin/users")
            .policy(adminPolicy)
            .to("bean:userService?method=getAllUsers");

        from("rest:get:/profile")
            .policy(userPolicy)
            .to("bean:userService?method=getCurrentUser");
    }
}
YAML
# Complete route configuration with Keycloak security
- onException:
    exception:
      - "org.apache.camel.CamelAuthorizationException"
    handled: true
    steps:
      - setHeader:
          name: "CamelHttpResponseCode"
          constant: 403
      - transform:
          constant: "Forbidden"

- route:
    from:
      uri: rest:get:/admin/users
      steps:
        - policy:
            ref: adminPolicy
        - to:
            uri: bean:userService?method=getAllUsers

- route:
    from:
      uri: rest:get:/profile
      steps:
        - policy:
            ref: userPolicy
        - to:
            uri: bean:userService?method=getCurrentUser

# Security policy beans
beans:
  - name: adminPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "{{keycloak.server-url}}"
      realm: "{{keycloak.realm}}"
      clientId: "{{keycloak.client-id}}"
      clientSecret: "{{keycloak.client-secret}}"
      requiredRoles: "admin"

  - name: userPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "{{keycloak.server-url}}"
      realm: "{{keycloak.realm}}"
      clientId: "{{keycloak.client-id}}"
      clientSecret: "{{keycloak.client-secret}}"
      requiredRoles: "user,admin"
      allRolesRequired: false

Running Integration Tests

The component includes integration tests that require a running Keycloak instance. These tests are disabled by default and only run when specific system properties are provided.

The integration tests include comprehensive testing for: * Role-based authorization with different role requirements * Permission-based authorization using custom claims and scopes * Public key verification with JWKS endpoint integration * Combined roles and permissions validation * Token parsing with and without public key verification * Different authorization header formats (Bearer token, custom header) * Token expiration and validity checks * Error handling for invalid tokens and insufficient privileges

Starting Keycloak with Docker

1. Start Keycloak Container

# Start Keycloak in development mode
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start-dev

2. Access Keycloak Admin Console

Open your browser to http://localhost:8080/admin and login with: - Username: admin - Password: admin

Keycloak Configuration for Integration Tests

3. Create Test Realm

  1. In the Keycloak Admin Console, click "Add realm"

  2. Set realm name to: test-realm

  3. Click "Create"

4. Create Test Client

  1. In the test-realm, go to Clients"Create client"

  2. Set the following:

    • Client type: OpenID Connect

    • Client ID: test-client

    • Next → Client authentication: ON

    • Authorization: ON (optional, for advanced features)

    • Next → Valid redirect URIs: *

    • Click "Save"

  3. Go to Credentials tab and copy the Client Secret

5. Create Test Roles

  1. Go to Realm roles"Create role"

  2. Create the following roles:

    • admin-role

    • user

    • reader

6. Create Test Users

Create three test users with the following configuration:

User 1: myuser 1. Go to Users"Add user" 2. Set: - Username: myuser - Email: myuser@test.com - First name: My - Last name: User - Click "Create" 3. Go to Credentials tab → "Set password" - Password: pippo123 - Temporary: OFF 4. Go to Role mapping tab → "Assign role" - Assign role: admin-role

User 2: test-user 1. Create user with: - Username: test-user - Password: user123 (temporary: OFF) - Assign role: user

User 3: reader-user 1. Create user with: - Username: reader-user - Password: reader123 (temporary: OFF) - Assign role: reader

Running the Integration Tests

7. Execute Tests with Maven

Run All Integration Tests:

# Run integration tests with required properties
mvn test -Dtest=KeycloakSecurityIT \
  -Dkeycloak.server.url=http://localhost:8080 \
  -Dkeycloak.realm=test-realm \
  -Dkeycloak.client.id=test-client \
  -Dkeycloak.client.secret=YOUR_CLIENT_SECRET

Run Specific Test Categories:

# Test only role-based authorization
mvn test -Dtest=KeycloakSecurityIT#testKeycloakSecurityPolicyWithValidAdminToken,testKeycloakSecurityPolicyWithValidUserToken,testKeycloakSecurityPolicyUserCannotAccessAdminRoute \
  -Dkeycloak.server.url=http://localhost:8080 \
  -Dkeycloak.realm=test-realm \
  -Dkeycloak.client.id=test-client \
  -Dkeycloak.client.secret=YOUR_CLIENT_SECRET

# Test only permissions-based authorization
mvn test -Dtest=KeycloakSecurityIT#testKeycloakSecurityPolicyWithPermissions,testKeycloakSecurityPolicyWithScopeBasedPermissions,testKeycloakSecurityPolicyWithCombinedRolesAndPermissions \
  -Dkeycloak.server.url=http://localhost:8080 \
  -Dkeycloak.realm=test-realm \
  -Dkeycloak.client.id=test-client \
  -Dkeycloak.client.secret=YOUR_CLIENT_SECRET

# Test only public key verification
mvn test -Dtest=KeycloakSecurityIT#testKeycloakSecurityPolicyWithPublicKeyVerification,testParseTokenDirectlyWithPublicKey \
  -Dkeycloak.server.url=http://localhost:8080 \
  -Dkeycloak.realm=test-realm \
  -Dkeycloak.client.id=test-client \
  -Dkeycloak.client.secret=YOUR_CLIENT_SECRET

Replace YOUR_CLIENT_SECRET with the actual client secret from step 4.

Run Manual Producer Tests:

The KeycloakProducerIT test contains manual integration tests for producer operations. These tests are disabled by default and require explicit activation:

# Run manual producer integration tests
mvn test -Dtest=KeycloakProducerIT \
  -Dmanual.keycloak.test=true \
  -Dkeycloak.server.url=http://localhost:8080 \
  -Dkeycloak.realm=master \
  -Dkeycloak.username=admin \
  -Dkeycloak.password=admin
The -Dmanual.keycloak.test=true flag is required to run KeycloakProducerIT tests. Without this flag, the tests will be skipped even if other Keycloak properties are provided. This prevents the tests from accidentally running in automated CI environments.

8. Alternative: Set Environment Variables

# For KeycloakSecurityIT tests
export KEYCLOAK_SERVER_URL=http://localhost:8080
export KEYCLOAK_REALM=test-realm
export KEYCLOAK_CLIENT_ID=test-client
export KEYCLOAK_CLIENT_SECRET=YOUR_CLIENT_SECRET

# Run security tests
mvn test -Dtest=KeycloakSecurityIT \
  -Dkeycloak.server.url=$KEYCLOAK_SERVER_URL \
  -Dkeycloak.realm=$KEYCLOAK_REALM \
  -Dkeycloak.client.id=$KEYCLOAK_CLIENT_ID \
  -Dkeycloak.client.secret=$KEYCLOAK_CLIENT_SECRET

# For manual producer tests (KeycloakProducerIT)
export KEYCLOAK_SERVER_URL=http://localhost:8080
export KEYCLOAK_REALM=master
export KEYCLOAK_USERNAME=admin
export KEYCLOAK_PASSWORD=admin

# Run manual producer tests (requires explicit flag)
mvn test -Dtest=KeycloakProducerIT \
  -Dmanual.keycloak.test=true \
  -Dkeycloak.server.url=$KEYCLOAK_SERVER_URL \
  -Dkeycloak.realm=$KEYCLOAK_REALM \
  -Dkeycloak.username=$KEYCLOAK_USERNAME \
  -Dkeycloak.password=$KEYCLOAK_PASSWORD

Troubleshooting

Tests are skipped: - For KeycloakSecurityIT: Verify all four required properties are provided and Keycloak is running on the specified URL. - For KeycloakProducerIT: Ensure -Dmanual.keycloak.test=true is set along with the required Keycloak properties.

401 Unauthorized: Check that: - Users exist with correct passwords - Users have the required roles assigned - Client credentials are correct

Connection refused: Ensure Keycloak is running and accessible at the specified URL.

Token validation errors: Verify the realm name and client configuration match exactly.

Setting up Permissions in Keycloak

For permissions-based authorization, you have several options to include permissions in tokens:

Option 1: Custom Claims Mapper

  1. In your realm, go to Client ScopesrolesMappersCreate mapper

  2. Set the following:

    • Mapper Type: User Attribute

    • Name: permissions-mapper

    • User Attribute: permissions

    • Token Claim Name: permissions

    • Claim JSON Type: JSON

    • Add to ID token: ON

    • Add to access token: ON

  3. Add the permissions attribute to users:

    • Go to Users → Select user → Attributes tab

    • Add attribute: permissions with value like ["read:documents", "write:documents"]

Option 2: Scope-based Permissions

  1. Configure client scopes:

    • Go to Client ScopesCreate client scope

    • Scope Name: documents

    • Protocol: openid-connect

  2. Add scope to client:

    • Go to Clients → Your client → Client Scopes tab

    • Add the scope as Default or Optional

  3. In your application code, you can then use scopes as permissions:

KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy();
policy.setRequiredPermissions("documents,users,admin");
policy.setAllPermissionsRequired(false); // ANY permission

Option 3: Authorization Services (Advanced)

For complex permission models, enable Keycloak Authorization Services:

  1. Go to Clients → Your client → SettingsAuthorization Enabled: ON

  2. Configure Resources, Scopes, and Policies in the Authorization tab

  3. Enable Authorization on the client

Note: Full Authorization Services integration requires additional setup and is more complex than the simple approaches above.

Combined Roles and Permissions Example

  • Java

  • YAML

// Create a policy that requires BOTH roles AND permissions
KeycloakSecurityPolicy strictPolicy = new KeycloakSecurityPolicy();
strictPolicy.setServerUrl("{{keycloak.server-url}}");
strictPolicy.setRealm("{{keycloak.realm}}");
strictPolicy.setClientId("{{keycloak.client-id}}");
strictPolicy.setClientSecret("{{keycloak.client-secret}}");

// User must have admin role AND document permissions (comma-separated)
strictPolicy.setRequiredRoles("admin");
strictPolicy.setRequiredPermissions("read:documents,write:documents");
strictPolicy.setAllRolesRequired(true);
strictPolicy.setAllPermissionsRequired(false); // ANY permission

// Create a policy that requires EITHER roles OR permissions
KeycloakSecurityPolicy flexiblePolicy = new KeycloakSecurityPolicy();
flexiblePolicy.setServerUrl("{{keycloak.server-url}}");
flexiblePolicy.setRealm("{{keycloak.realm}}");
flexiblePolicy.setClientId("{{keycloak.client-id}}");
flexiblePolicy.setClientSecret("{{keycloak.client-secret}}");

// Apply different policies to different routes
from("direct:admin-documents")
    .policy(strictPolicy)
    .to("bean:documentService?method=adminOperations");

from("direct:flexible-access")
    .policy(flexiblePolicy)
    .to("bean:documentService?method=flexibleOperations");
- route:
    from:
      uri: direct:admin-documents
      steps:
        - policy:
            ref: strictPolicy
        - to:
            uri: bean:documentService?method=adminOperations

- route:
    from:
      uri: direct:flexible-access
      steps:
        - policy:
            ref: flexiblePolicy
        - to:
            uri: bean:documentService?method=flexibleOperations

# Bean definitions
beans:
  - name: strictPolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "{{keycloak.server-url}}"
      realm: "{{keycloak.realm}}"
      clientId: "{{keycloak.client-id}}"
      clientSecret: "{{keycloak.client-secret}}"
      requiredRoles: "admin"
      requiredPermissions: "read:documents,write:documents"
      allRolesRequired: true
      allPermissionsRequired: false

  - name: flexiblePolicy
    type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
    properties:
      serverUrl: "{{keycloak.server-url}}"
      realm: "{{keycloak.realm}}"
      clientId: "{{keycloak.client-id}}"
      clientSecret: "{{keycloak.client-secret}}"
      requiredRoles: "admin,manager"
      requiredPermissions: "read:documents,emergency:access"
      allRolesRequired: false
      allPermissionsRequired: false