Managing Keycloak configuration programmatically

December 11, 2020 

Keycloak is an excellent Open Source Identity and Access Management solution that builds on top of the Wildfly application server. We manage several Keycloak installations for providing SSO with FreeIPA credentials for self-hosted and SaaS services via SAML and OIDC.

To keep our Keycloak configurations healthy and in a known-good state we manage their configurations programmatically. As we use Puppet for our configuration management needs we've contributed a lot to the treydock/puppet-module-keycloak project. There is also a Terraform Keycloak provider that covers the Keycloak API very well, but it won't help you one bit with setting up Keycloak initially. This can be problematic if you're setting up something more complex like clustered domain mode with TLS reverse proxies.

On a more general level when you manage Keycloak configurations programmatically you have two options:

The kcadm.sh verbs map to HTTP request methods like this (from here):

  • POST: kcadm.sh create
  • GET: kcadm.sh get
  • PUT: kcadm.sh update
  • DELETE: kcadm.sh delete

This part is easy. But how do you figure out the correct endpoint and the contents for the payload? Trying to construct those yourself based on the Keycloak Admin REST API documentation is challenging to say the least.

Fortunately there is an easier way, by using the Keycloak Admin UI and Chrome DevTools, which will tell what HTTP requests are made by the Keycloak Admin UI when it does the operation you're interested in. This way you get the request method, endpoint and payload trivially. You also get the requests the Admin UI uses to figure out the current state of the resources. Armed with this information you can start experimenting with kcadm.sh or direct API calls and see what happens.

For GET requests it is usually sufficient to look at the URL in the Keycloak Admin UI. Say you're at this URL which shows you information about a user:

  • http://localhost:8080/auth/admin/master/console/#/realms/foobar/users/cadb1c76-b718-45e2-9609-ace23e1fd860

This can be trivally converted into a kcadm.sh call:

kcadm.sh get users/cadb1c76-b718-45e2-9609-ace23e1fd860 -r foobar

Requests that change resources in Keycloak need one of two things:

  1. Command-line parameters that tell what should change
  2. Proper URL, method and JSON payload

And example of #1 is adding a client role to a service account user with add-roles command:

kcadm.sh add-roles -r foobar --uusername service-account-foobar --cclientid foobar --rolename offline_access

For method #2 you simply copy/adapt the URL, request method and payload you obtained from the browser to the kcadm.sh call. For example the HTTP request to add a realm role to a user is

Request URL: http://localhost:8080/auth/admin/realms/foobar/users/5bc5a439-9571-4199-890f-2fdb4c5f4a32/role-mappings/clients/e906db94-5344-4437-8091-881a6f8f886d

Request Method: POST

Payload:
[
  {
    "id":"73e292f3-61c4-40ef-b53c-9ea046930312",
    "name":"testclient-role",
    "composite":false,
    "clientRole":true,
    "containerId":"e906db94-5344-4437-8091-881a6f8f886d"
  }
]

Where the "containerId" is the client's ID and "id" is ID of the client role. This maps to kcadm.sh call like this:

/opt/keycloak/bin/kcadm-wrapper.sh create users/5bc5a439-9571-4199-890f-2fdb4c5f4a32/role-mappings/clients/e906db94-5344-4437-8091-881a6f8f886d -r foobar -f payload.json

The payload.json will be identical to the one shown for the direct API call.

One further note about the "id" parameters in Keycloak resources is in order. When you create resources such as clients, realm or users with the Keycloak Admin UI they will get a long, randomized identifier and there's nothing you can do about it. If you create the same resources programmatically you can define the identifier yourself, which makes your configurations more readable and more portable as they won't be tied to any specific Keycloak instance. You just need to be careful not to create resources with duplicate "id".

Samuli Seppänen
Samuli Seppänen
Author archive
menucross-circle