Automating Keycloak with Java

We needed to automate some tasks in our Keycloak so that some elements were created as events were happening.

I find that the Java library for Keycloak is not always intuitive. It does not help that my searches for the documentation or examples returned all sorts of contradicting results.

In this case adding an Identity Provider seems to have many ways to be done but only 1 worked for me.

What works:

destinationRealmResource.identityProviders().create(identityProviderRepresentation);

Alternative code I had seen that I could not get to work for me:

destinationRealmResource.toRepresentation().addIdentityProvider(identityProviderRepresentation);
var clientRes = client.updateRealm(destinationRealmResource.toRepresentation());

And this did not work for me:

destinationRealmResource.toRepresentation().setIdentityProviders(List.of(identityProviderRepresentation));
var clientRes = client.updateRealm(destinationRealmResource.toRepresentation());

This was done with Keycloak 15.

In my troubleshooting I also noticed that having some invalid strings in your representation configuration can cause the creation to fail. I am not a fan of the Map<String, String> for the configuration because that leaves room for a lot of mistakes.

Keycloak Identity Provider ID

That took a couple of hours to figure out so let’s share some notes.

I have code that creates the required Keycloak Realms and configures a few items in each of them.

One of those configuration is to create the Identity Provider so that we can link users to another realm. Internal SSO if you will.

I tested this with Keycloak 11.0.3.

I did not understand that the Identity Provider ID needed to be one of the specific values since it is only a string that you pass to it. Sometimes I wish they could use an enum to help users make sense of it quickly.

After getting errors that it was a required fields and trying a few values that did not make sense to Keycloak.

10:39:07.587 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "{"errorMessage":"Invalid identity provider id [password]"}"

10:41:50.814 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "{"errorMessage":"Invalid identity provider id [EmailAuthProviderID]"}"

I tried keycloak-oidc and it worked. So the value needs to be one of these:

  • saml
  • oidc
  • keycloak-oidc

You could also use one of the social identity provider.

I also understood to take the config from the endpoint:

http://192.168.1.50:8080/auth/realms/Customer/.well-known/openid-configuration

Sample code:

IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation();
identityProviderRepresentation.setDisplayName(IDP);
identityProviderRepresentation.setProviderId("keycloak-oidc");
identityProviderRepresentation.setAlias(IDP);
Map<String, String> config = new HashMap<>();
config.put("issuer", "http://192.168.1.50:8080/auth/realms/Customer");
config.put("authorization_endpoint", "http://192.168.1.50:8080/auth/realms/Customer/protocol/openid-connect/auth");
config.put("token_endpoint", "http://192.168.1.50:8080/auth/realms/Customer/protocol/openid-connect/token");
config.put("introspection_endpoint", "http://192.168.1.50:8080/auth/realms/Customer/protocol/openid-connect/token/introspect");
config.put("userinfo_endpoint", "http://192.168.1.50:8080/auth/realms/Customer/protocol/openid-connect/userinfo");
config.put("end_session_endpoint", "http://192.168.1.50:8080/auth/realms/Customer/protocol/openid-connect/logout");
config.put("jwks_uri", "http://192.168.1.50:8080/auth/realms/Customer/protocol/openid-connect/certs");
config.put("check_session_iframe", "http://192.168.1.50:8080/auth/realms/Customer/protocol/openid-connect/login-status-iframe.html");
config.put("grant_types_supported", "[\"authorization_code\",\"implicit\",\"refresh_token\",\"password\",\"client_credentials\"]");
config.put("response_types_supported", "[\"code\",\"none\",\"id_token\",\"token\",\"id_token token\",\"code id_token\",\"code token\",\"code id_token token\"]");
config.put("subject_types_supported", "[\"public\",\"pairwise\"]");
config.put("id_token_signing_alg_values_supported", "[\"PS384\",\"ES384\",\"RS384\",\"HS256\",\"HS512\",\"ES256\",\"RS256\",\"HS384\",\"ES512\",\"PS256\",\"PS512\",\"RS512\"]");
config.put("id_token_encryption_alg_values_supported", "[\"RSA-OAEP\",\"RSA1_5\"]");
config.put("id_token_encryption_enc_values_supported", "[\"A256GCM\",\"A192GCM\",\"A128GCM\",\"A128CBC-HS256\",\"A192CBC-HS384\",\"A256CBC-HS512\"]");
config.put("userinfo_signing_alg_values_supported", "[\"PS384\",\"ES384\",\"RS384\",\"HS256\",\"HS512\",\"ES256\",\"RS256\",\"HS384\",\"ES512\",\"PS256\",\"PS512\",\"RS512\",\"none\"]");
config.put("request_object_signing_alg_values_supported", "[\"PS384\",\"ES384\",\"RS384\",\"HS256\",\"HS512\",\"ES256\",\"RS256\",\"HS384\",\"ES512\",\"PS256\",\"PS512\",\"RS512\",\"none\"]");
config.put("response_modes_supported", "[\"query\",\"fragment\",\"form_post\"]");
config.put("registration_endpoint", "http://192.168.1.50:8080/auth/realms/Customer/clients-registrations/openid-connect");
config.put("token_endpoint_auth_methods_supported", "[\"private_key_jwt\",\"client_secret_basic\",\"client_secret_post\",\"tls_client_auth\",\"client_secret_jwt\"]");
config.put("token_endpoint_auth_signing_alg_values_supported", "[\"PS384\",\"ES384\",\"RS384\",\"HS256\",\"HS512\",\"ES256\",\"RS256\",\"HS384\",\"ES512\",\"PS256\",\"PS512\",\"RS512\"]");
config.put("claims_supported", "[\"aud\",\"sub\",\"iss\",\"auth_time\",\"name\",\"given_name\",\"family_name\",\"preferred_username\",\"email\",\"acr\"]");
config.put("claim_types_supported", "[\"normal\"]");
config.put("claims_parameter_supported", "false");
config.put("scopes_supported", "[\"openid\",\"offline_access\", \"profile\",\"email\",\"address\",\"phone\",\"roles\",\"web-origins\",\"microprofile-jwt\"]");
config.put("request_parameter_supported", "true");
config.put("request_uri_parameter_supported", "true");
config.put("code_challenge_methods_supported", "[\"plain\",\"S256\"]");
config.put("tls_client_certificate_bound_access_tokens", "true");
identityProviderRepresentation.setConfig(config);

Finally, I have creation of the Identity Provider in my realm and I can link users to it.