Keycloak

Since Camel 4.15

Only producer is 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 12 options, which are listed below.

Name Description Default Type

clientId (producer)

Keycloak client ID.

String

clientSecret (producer)

Keycloak client secret.

String

configuration (producer)

Component configuration.

KeycloakConfiguration

keycloakClient (producer)

Autowired To use an existing configured Keycloak admin client.

Keycloak

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

operation (producer)

The operation to perform.

Enum values:

  • createRealm

  • deleteRealm

  • getRealm

  • updateRealm

  • createUser

  • deleteUser

  • getUser

  • updateUser

  • listUsers

  • createRole

  • deleteRole

  • getRole

  • updateRole

  • listRoles

  • assignRoleToUser

  • removeRoleFromUser

KeycloakOperations

password (producer)

Keycloak password.

String

pojoRequest (producer)

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

false

boolean

realm (producer)

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

master

String

serverUrl (producer)

Keycloak server URL.

String

username (producer)

Keycloak username.

String

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 (producer)

Required Logical name.

String

Query Parameters (10 parameters)

Name Description Default Type

clientId (producer)

Keycloak client ID.

String

clientSecret (producer)

Keycloak client secret.

String

keycloakClient (producer)

Autowired To use an existing configured Keycloak admin client.

Keycloak

operation (producer)

The operation to perform.

Enum values:

  • createRealm

  • deleteRealm

  • getRealm

  • updateRealm

  • createUser

  • deleteUser

  • getUser

  • updateUser

  • listUsers

  • createRole

  • deleteRole

  • getRole

  • updateRole

  • listRoles

  • assignRoleToUser

  • removeRoleFromUser

KeycloakOperations

password (producer)

Keycloak password.

String

pojoRequest (producer)

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

false

boolean

realm (producer)

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

master

String

serverUrl (producer)

Keycloak server URL.

String

username (producer)

Keycloak username.

String

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

Producer Operations

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

Configuration

To use the producer, configure the Keycloak connection details:

  • Java

  • YAML

// 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);
# Configuration in application.yaml
camel:
  component:
    keycloak:
      server-url: "http://localhost:8080"
      realm: "master"
      username: "admin"
      password: "admin"

Supported Operations

The component supports the following operations:

  • Realm Management: createRealm, getRealm, deleteRealm

  • User Management: createUser, listUsers, deleteUser, setUserPassword

  • Role Management: createRole, getRole, deleteRole, assignRoleToUser

  • Client Management: createClient, getClient, getClientSecret, deleteClient

Realm Operations

  • Java

  • YAML

// 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");
- route:
    from:
      uri: direct:create-realm
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-new-realm"
        - to:
            uri: keycloak:admin?operation=createRealm
        - log: "Created realm: ${header.CamelKeycloakRealmName}"

- route:
    from:
      uri: direct:get-realm
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - to:
            uri: keycloak:admin?operation=getRealm
        - log: "Realm info: ${body}"

- route:
    from:
      uri: direct:delete-realm
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-old-realm"
        - to:
            uri: keycloak:admin?operation=deleteRealm
        - log: "Deleted realm: ${header.CamelKeycloakRealmName}"

User Operations

  • Java

  • YAML

// 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);
# Create user route
- route:
    from:
      uri: direct:create-user
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUsername
            simple: "${body[username]}"
        - setHeader:
            name: CamelKeycloakUserEmail
            simple: "${body[email]}"
        - setHeader:
            name: CamelKeycloakUserFirstName
            simple: "${body[firstName]}"
        - setHeader:
            name: CamelKeycloakUserLastName
            simple: "${body[lastName]}"
        - to:
            uri: keycloak:admin?operation=createUser
        - log: "Created user: ${header.CamelKeycloakUsername}"

# Set user password route
- route:
    from:
      uri: direct:set-user-password
      steps:
        - setHeader:
            name: CamelKeycloakRealmName
            constant: "my-realm"
        - setHeader:
            name: CamelKeycloakUsername
            simple: "${body[username]}"
        - setHeader:
            name: CamelKeycloakUserPassword
            simple: "${body[password]}"
        - setHeader:
            name: CamelKeycloakUserPasswordTemporary
            constant: false
        - to:
            uri: keycloak:admin?operation=setUserPassword
        - log: "Set password for user: ${header.CamelKeycloakUsername}"

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

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

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}"

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"

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
policy.setRequiredRoles(Arrays.asList("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
policy.setRequiredPermissions(Arrays.asList("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

[]

List of required roles

requiredPermissions

[]

List of required permissions

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
keycloakPolicy.setRequiredRoles(Arrays.asList("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
KeycloakSecurityPolicy userPolicy = new KeycloakSecurityPolicy(
    "http://localhost:8080", "my-company", "my-service", "client-secret");
userPolicy.setRequiredRoles(Arrays.asList("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(Arrays.asList("reader", "writer", "admin"));
readPolicy.setAllRolesRequired(false);

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

KeycloakSecurityPolicy adminPolicy = new KeycloakSecurityPolicy(
    "{{keycloak.server-url}}", "{{keycloak.realm}}",
    "{{keycloak.client-id}}", "{{keycloak.client-secret}}");
adminPolicy.setRequiredRoles(Arrays.asList("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(Arrays.asList("admin"));
        return policy;
    }

    @Bean
    public KeycloakSecurityPolicy userPolicy() {
        KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(
            serverUrl, realm, clientId, clientSecret);
        policy.setRequiredRoles(Arrays.asList("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(Arrays.asList("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(Arrays.asList("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.

8. Alternative: Set Environment Variables

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 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

Troubleshooting

Tests are skipped: Verify all four required properties are provided and Keycloak is running on the specified URL.

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(Arrays.asList("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
strictPolicy.setRequiredRoles(Arrays.asList("admin"));
strictPolicy.setRequiredPermissions(Arrays.asList("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