Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Last active April 30, 2024 22:18
Show Gist options
  • Save thomasdarimont/c4e739c5a319cf78a4cff3b87173a84b to your computer and use it in GitHub Desktop.
Save thomasdarimont/c4e739c5a319cf78a4cff3b87173a84b to your computer and use it in GitHub Desktop.
Using Keycloak Admin Client to create user with roles (Realm and Client level)
package demo.plain;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.CreatedResponseUtil;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import javax.ws.rs.core.Response;
import java.util.Arrays;
import java.util.Collections;
public class KeycloakAdminClientExample {
public static void main(String[] args) {
String serverUrl = "http://sso.tdlabs.local:8899/u/auth";
String realm = "acme";
// idm-client needs to allow "Direct Access Grants: Resource Owner Password Credentials Grant"
String clientId = "idm-client";
String clientSecret = "0d61686d-57fc-4048-b052-4ce74978c468";
// // Client "idm-client" needs service-account with at least "manage-users, view-clients, view-realm, view-users" roles for "realm-management"
// Keycloak keycloak = KeycloakBuilder.builder() //
// .serverUrl(serverUrl) //
// .realm(realm) //
// .grantType(OAuth2Constants.CLIENT_CREDENTIALS) //
// .clientId(clientId) //
// .clientSecret(clientSecret).build();
// User "idm-admin" needs at least "manage-users, view-clients, view-realm, view-users" roles for "realm-management"
Keycloak keycloak = KeycloakBuilder.builder() //
.serverUrl(serverUrl) //
.realm(realm) //
.grantType(OAuth2Constants.PASSWORD) //
.clientId(clientId) //
.clientSecret(clientSecret) //
.username("idm-admin") //
.password("admin") //
.build();
// Define user
UserRepresentation user = new UserRepresentation();
user.setEnabled(true);
user.setUsername("tester1");
user.setFirstName("First");
user.setLastName("Last");
user.setEmail("tom+tester1@tdlabs.local");
user.setAttributes(Collections.singletonMap("origin", Arrays.asList("demo")));
// Get realm
RealmResource realmResource = keycloak.realm(realm);
UsersResource usersRessource = realmResource.users();
// Create user (requires manage-users role)
Response response = usersRessource.create(user);
System.out.printf("Repsonse: %s %s%n", response.getStatus(), response.getStatusInfo());
System.out.println(response.getLocation());
String userId = CreatedResponseUtil.getCreatedId(response);
System.out.printf("User created with userId: %s%n", userId);
// Define password credential
CredentialRepresentation passwordCred = new CredentialRepresentation();
passwordCred.setTemporary(false);
passwordCred.setType(CredentialRepresentation.PASSWORD);
passwordCred.setValue("test");
UserResource userResource = usersRessource.get(userId);
// Set password credential
userResource.resetPassword(passwordCred);
// // Get realm role "tester" (requires view-realm role)
RoleRepresentation testerRealmRole = realmResource.roles()//
.get("tester").toRepresentation();
//
// // Assign realm role tester to user
userResource.roles().realmLevel() //
.add(Arrays.asList(testerRealmRole));
//
// // Get client
ClientRepresentation app1Client = realmResource.clients() //
.findByClientId("app-frontend-springboot").get(0);
//
// // Get client level role (requires view-clients role)
RoleRepresentation userClientRole = realmResource.clients().get(app1Client.getId()) //
.roles().get("user").toRepresentation();
//
// // Assign client level role to user
userResource.roles() //
.clientLevel(app1Client.getId()).add(Arrays.asList(userClientRole));
// Send password reset E-Mail
// VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD, TERMS_AND_CONDITIONS
// usersRessource.get(userId).executeActionsEmail(Arrays.asList("UPDATE_PASSWORD"));
// Delete User
// userResource.remove();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.thomasdarimont.keycloak</groupId>
<artifactId>keycloak-admin-client-example</artifactId>
<version>1.0.0.0-SNAPSHOT</version>
<properties>
<keycloak.version>8.0.2</keycloak.version>
<resteasy.version>3.9.1.Final</resteasy.version>
</properties>
<dependencies>
<dependency>
<artifactId>keycloak-admin-client</artifactId>
<groupId>org.keycloak</groupId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
</dependencies>
</project>
@drnow4u
Copy link

drnow4u commented Jun 3, 2022

Hi,
Thank you for helpful description and comments.
Do you know how to assign Service Account Roles with KeycloakBuilder? I would like to use it for tests.

image

@drnow4u
Copy link

drnow4u commented Jun 3, 2022

This solution Keycloak: Add Client Roles to Service Account Roles with Java API client is working for me. Where platform-administration is a client to update Service Account Roles.

@kashifJawed90
Copy link

I'm getting error on creating keycloakAdmin :

keycloakClient = KeycloakBuilder.builder() // .serverUrl(KC_ADMIN_CLIENT_SERVER_URL) // .realm(KC_ADMIN_CLIENT_REALM) // .grantType(OAuth2Constants.CLIENT_CREDENTIALS) // .clientId(KC_ADMIN_CLIENT_ID) // .clientSecret(KC_ADMIN_CLIENT_SECRET) // .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build()) .build();
POM.xml
<dependency> <artifactId>keycloak-admin-client</artifactId> <groupId>org.keycloak</groupId> <version>${keycloak.version}</version> </dependency>

java.lang.NoClassDefFoundError: org/keycloak/admin/client/KeycloakBuilder
at com.iotech.intellija.common.KeyCloakImpl.KeycloakAdminClientImpl.lambda$init$0(KeycloakAdminClientImpl.java:56)
at io.reactivex.internal.operators.completable.CompletableFromCallable.subscribeActual(CompletableFromCallable.java:36)
at io.reactivex.Completable.subscribe(Completable.java:2302)
at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
at io.reactivex.Completable.subscribe(Completable.java:2302)
at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$SourceObserver.onComplete(CompletableAndThenCompletable.java:67)
at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$NextObserver.onComplete(CompletableAndThenCompletable.java:99)
at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115)
at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$NextObserver.onComplete(CompletableAndThenCompletable.java:99)
at io.vertx.reactivex.impl.AsyncResultCompletable.lambda$subscribeActual$0(AsyncResultCompletable.java:48)
at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23)
at io.vertx.core.Promise.tryComplete(Promise.java:121)
at io.vertx.core.Promise.complete(Promise.java:77)
at io.vertx.rabbitmq.impl.RabbitMQClientImpl.lambda$tryConnect$37(RabbitMQClientImpl.java:744)
at io.vertx.core.impl.future.FutureImpl$1.onSuccess(FutureImpl.java:91)
at io.vertx.core.impl.future.FutureImpl$ListenerArray.onSuccess(FutureImpl.java:262)
at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23)
at io.vertx.core.impl.future.PromiseImpl.onSuccess(PromiseImpl.java:49)
at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
at io.vertx.core.impl.future.FutureImpl.addListener(FutureImpl.java:196)
at io.vertx.core.impl.future.PromiseImpl.addListener(PromiseImpl.java:23)
at io.vertx.core.impl.future.FutureImpl.onComplete(FutureImpl.java:164)
at io.vertx.core.impl.future.PromiseImpl.onComplete(PromiseImpl.java:23)
at io.vertx.rabbitmq.impl.RabbitMQClientImpl.lambda$tryConnect$36(RabbitMQClientImpl.java:738)
at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
at io.vertx.core.impl.TaskQueue.run(TaskQueue.java:76)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:750)
Caused by: java.lang.ClassNotFoundException: org.keycloak.admin.client.KeycloakBuilder
at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 40 common frames omitted

@shanuchauhan94
Copy link

shanuchauhan94 commented Feb 28, 2023

RoleRepresentation userRealmRole = new RoleRepresentation();
if (region.equalsIgnoreCase("india")) {
userRealmRole = realmResource.roles().get("user-ind").toRepresentation();
} else if (region.equalsIgnoreCase("us")) {
userRealmRole = realmResource.roles().get("user-us").toRepresentation();
}
UserResource userResource = usersResource.get(userId);
// Assign realm role 'user-ind' to user
userResource.roles().realmLevel().add(Collections.singletonList(userRealmRole));

    // Get client level role (requires view-clients role)
    List<ClientRepresentation> clientRepresentation = realmResource.clients().findByClientId(provider.CLIENT_ID);
    clientRepresentation.forEach(cr -> {
        RoleRepresentation roleRepresentation = realmResource.clients().get(cr.getId()).roles().get("USER").toRepresentation();
        userResource.roles().clientLevel(cr.getId()).add(List.of(roleRepresentation));
    });
    // Send password reset E-Mail
    usersResource.get(userId).executeActionsEmail(List.of("UPDATE_PASSWORD"));

in order to access these functionality you have to map user with few addition role as:
manage-realm
manage-users
manage-clients

@gilamarko
Copy link

Hello! First of all thanks for the provided code, I have a question, is there any official documentation of how to use the
org.keycloak:keycloak-admin-client ??

For sure your example is great but if I hadn't found this, it would be a nightmare to learn how to use it on the fly.

@ghilainm
Copy link

@thomasdarimont Isn't it possible to do it in one shot? Assign password and roles when creating the user? I tried but it seems not to work...

@AndreaNicola
Copy link

AndreaNicola commented Jan 30, 2024 via email

@ghilainm
Copy link

@AndreaNicola Credentials seem to work for me!

@hyenicil
Copy link

Hello, how do I use keycloak for its new versions?

@maj2c
Copy link

maj2c commented Apr 23, 2024

If you still getting "HTTP 400 Bad request" error after following the tips and hints in this ticket or it suddenly stopped working, make sure the user account you are using does not have unresolved required actions such as verify email, fill in first name and last name etc. because you can't invoke direct grant if there are unresolved required actions!

I am using the latest version (24.0.3) at the time of writing this comment.

@flowt-au
Copy link

@maj2c Yes! That was it! Since the Firstname and Lastname fields are not marked as required in the Keycloak UI, I didn't bother filling them in. And, testing shows if either of these are missing it throws a 400! I think that is probably a bug? No?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment