I am new to Keycloak and trying to wrap my head around how to properly register a user using the Keycloack admin client
The documentation doesn't have concrete examples and there a ton of screen shots missing that perpetuate ambiguity.
I have found two JAVA based examples which provide some insight on how the API calls should work: here and here but I keep encountering a jakarta.ws.rs.NotFoundException: HTTP 404 Not Found every time I try to create a user or view the realm representation. Any help on why this could be happening?
I have created a Client Credentials client and assigned the following roles to be able to manage users (create, delete, modify).
I am able to receive a token from the Token end-point using the Client Credentials flow in POSTMAN
Below you will find the Java calls
private Keycloak getAdminKeycloak() {
this.base = environment.getProperty(MyConstants.KEYCLOAK_TOKEN_END_POINT);
this.realm = environment.getProperty(MyConstants.KEYCLOAK_REALM);
this.username = environment.getProperty(MyConstants.KEYCLOAK_SERVER_API_CLIENTID);
this.password = environment.getProperty(MyConstants.KEYCLOAK_SERVICE_API_SECRET);
return KeycloakBuilder.builder().serverUrl(base)
.realm(realm)
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(username)
.clientSecret(password).build();
}
@Override
public ResponseEntity<String> registerUser(User user) {
Keycloak keycloak = getAdminKeycloak();
// set user representation
UserRepresentation newuser = new UserRepresentation();
newuser.setEmail(user.getEmail());
newuser.setFirstName(user.getFirstName());
newuser.setLastName(user.getLastName());
newuser.setEnabled(true);
// Get realm
RealmResource realmResource = keycloak.realm(realm);
System.out.print(realmResource.toRepresentation().toString());
UsersResource usersResource = realmResource.users();
ArrayList<UserRepresentation> users = (ArrayList<UserRepresentation>) usersResource.list();
// create user
Response response = usersResource.create(newuser);
String userID = CreatedResponseUtil.getCreatedId(response);
if (userID == null || userID.isEmpty()) {
return new ResponseEntity<>(MyConstants.ERROR_OCCURED, HttpStatus.METHOD_FAILURE);
}
// set credentials
CredentialRepresentation passwordCred = new CredentialRepresentation();
passwordCred.setTemporary(false);
passwordCred.setType(CredentialRepresentation.PASSWORD);
passwordCred.setValue(user.getPassword());
UserResource userResource = usersResource.get(userID);
userResource.resetPassword(passwordCred);
return new ResponseEntity<>(MyConstants.REGISTRATION_COMPLETE, HttpStatus.OK);
}
Maven 3.9.9 and Java 17
More detail for keycloak docker launching in here
docker-compose.yml
version: '3.6'
services:
keycloak_web:
image: quay.io/keycloak/keycloak:23.0.7
container_name: keycloak_web
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloakdb:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: password
KC_HOSTNAME: localhost
KC_HOSTNAME_PORT: 8080
KC_HOSTNAME_STRICT: false
KC_HOSTNAME_STRICT_HTTPS: false
KC_LOG_LEVEL: info
KC_METRICS_ENABLED: true
KC_HEALTH_ENABLED: true
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
command: start-dev
depends_on:
- keycloakdb
ports:
- 8080:8080
keycloakdb:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
volumes:
postgres_data:
More detail got master token in here
Test Client Token by curl
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=my-client" \
-d "client_secret=hzkBBj8MY8hDaqHblrSkKbaDQvUnhbyb" \
-d "grant_type=client_credentials"
Files tree
C:\Users\benchvue\temp\6>tree /F
Folder PATH listing
Volume serial number is 8837-26D8
C:.
│ docker-compose.yml
│ pom.xml
│
└───src
└───main
├───java
│ └───com
│ └───example
│ KeycloakUserRegistrationApplication.java
│ KeycloakUserService.java
│ User.java
│
└───resources
application.properties
KeycloakUserRegistrationApplication.java
package com.example;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
@SpringBootApplication
public class KeycloakUserRegistrationApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(KeycloakUserRegistrationApplication.class)
.properties("server.port=8081")
.run(args);
}
@Bean
public CommandLineRunner run(KeycloakUserService keycloakUserService) {
return args -> {
User newUser = new User();
newUser.setUsername("testuser"); // Setting the required username
newUser.setEmail("testuser@example.com");
newUser.setFirstName("John");
newUser.setLastName("Kim");
newUser.setPassword("password123"); // Example password, adjust as needed
keycloakUserService.registerUser(newUser);
};
}
}
KeycloakUserService.java
package com.example;
import org.keycloak.OAuth2Constants;
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.UsersResource;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.ws.rs.core.Response;
@Service
public class KeycloakUserService {
@Autowired
private Environment environment;
private Keycloak getAdminKeycloak() {
String serverUrl = environment.getProperty("keycloak.server-url");
String masterRealm = environment.getProperty("keycloak.master-realm");
String clientId = environment.getProperty("keycloak.master-client-id");
String clientSecret = environment.getProperty("keycloak.master-client-secret");
System.out.println("Server URL: " + serverUrl);
System.out.println("Master Realm: " + masterRealm);
System.out.println("Client ID: " + clientId);
System.out.println("Client Secret: " + clientSecret);
// Using the password grant type for admin client
return KeycloakBuilder.builder()
.serverUrl(serverUrl)
.realm(masterRealm)
.grantType(OAuth2Constants.PASSWORD)
.clientId("admin-cli")
.username("admin") // Ensure this matches the username in Keycloak
.password("admin") // Ensure this matches the password in Keycloak
.build();
}
private Keycloak getClientKeycloak() {
String serverUrl = environment.getProperty("keycloak.server-url");
String targetRealm = environment.getProperty("keycloak.target-realm");
String clientId = environment.getProperty("keycloak.client-id");
String clientSecret = environment.getProperty("keycloak.client-secret");
System.out.println("Target Realm Keycloak Config:");
System.out.println("serverUrl: " + serverUrl);
System.out.println("targetRealm: " + targetRealm);
System.out.println("clientId: " + clientId);
System.out.println("clientSecret: " + clientSecret);
// Using client_credentials for non-admin clients
Keycloak keycloak = KeycloakBuilder.builder()
.serverUrl(serverUrl)
.realm(targetRealm)
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(clientId)
.clientSecret(clientSecret)
.build();
try {
AccessTokenResponse tokenResponse = keycloak.tokenManager().getAccessToken();
System.out.println("Access Token for client: " + tokenResponse.getToken());
} catch (Exception e) {
System.out.println("Failed to retrieve client access token: " + e.getMessage());
}
return keycloak;
}
public ResponseEntity<String> registerUser(User user) {
Keycloak keycloak = getAdminKeycloak(); // Using admin Keycloak instance
String targetRealm = environment.getProperty("keycloak.target-realm");
System.out.println("Using target realm: " + targetRealm);
System.out.println("Creating user with email: " + user.getEmail());
UserRepresentation newUser = new UserRepresentation();
newUser.setUsername(user.getUsername()); // Ensure username is set here
newUser.setEmail(user.getEmail());
newUser.setFirstName(user.getFirstName());
newUser.setLastName(user.getLastName());
newUser.setEnabled(true);
RealmResource realmResource = keycloak.realm(targetRealm);
UsersResource usersResource = realmResource.users();
Response response = usersResource.create(newUser);
int status = response.getStatus();
System.out.println("Response status: " + status);
if (status == 201) {
return new ResponseEntity<>("User registration completed successfully.", HttpStatus.CREATED);
} else {
String errorResponse = response.readEntity(String.class);
System.out.println("Response: " + errorResponse);
return new ResponseEntity<>("An error occurred while creating the user: " + errorResponse, HttpStatus.BAD_REQUEST);
}
}
}
application.properties
# Keycloak server URL
keycloak.server-url=http://localhost:8080
keycloak.master-realm=master
keycloak.master-client-id=admin
keycloak.master-client-secret=admin
keycloak.target-realm=my-realm
keycloak.client-id=my-client
keycloak.client-secret=hzkBBj8MY8hDaqHblrSkKbaDQvUnhbyb
# Messages for registration status
registration.complete=User registration completed successfully.
error.occurred=An error occurred while creating the user.
User.java
package com.example;
public class User {
private String username;
private String email;
private String password;
private String firstName;
private String lastName;
// Getter and setter for username
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
// Getter and setter for email
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
// Getter and setter for password
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
// Getter and setter for firstName
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
// Getter and setter for lastName
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
pom.xml
<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.example</groupId>
<artifactId>keycloak-user-registration</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.1.0</spring-boot.version>
<keycloak.version>23.0.7</keycloak.version>
</properties>
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Keycloak Admin Client (downgraded to 18.0.0) -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>18.0.0</version>
</dependency>
<!-- Javax JAX-RS API (for javax.ws.rs.core) -->
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1.1</version>
</dependency>
<!-- Logback for logging compatibility -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.7</version>
</dependency>
<!-- Lombok (Optional for Getter/Setter) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!-- Testing Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.14.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.14.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.example.KeycloakUserRegistrationApplication</mainClass>
</configuration>
</plugin>
<!-- Compiler Plugin for Java 17 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
mvn clena compile
mvn clean package
java -jar target/keycloak-user-registration-1.0-SNAPSHOT.jar
KeycloakUserService.java
package com.example;
import org.keycloak.OAuth2Constants;
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.UsersResource;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.ws.rs.core.Response;
import org.keycloak.admin.client.resource.UserResource;
@Service
public class KeycloakUserService {
@Autowired
private Environment environment;
private Keycloak getClientKeycloak() {
String serverUrl = environment.getProperty("keycloak.server-url");
String targetRealm = environment.getProperty("keycloak.target-realm");
String clientId = environment.getProperty("keycloak.client-id");
String clientSecret = environment.getProperty("keycloak.client-secret");
System.out.println("Target Realm Keycloak Config:");
System.out.println("serverUrl: " + serverUrl);
System.out.println("targetRealm: " + targetRealm);
System.out.println("clientId: " + clientId);
System.out.println("clientSecret: " + clientSecret);
// Building Keycloak instance using client credentials
Keycloak keycloak = KeycloakBuilder.builder()
.serverUrl(serverUrl)
.realm(targetRealm) // Set to 'my-realm'
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(clientId) // Set to 'my-client'
.clientSecret(clientSecret) // Set to 'my-client' secret
.build();
try {
AccessTokenResponse tokenResponse = keycloak.tokenManager().getAccessToken();
System.out.println("Access Token for client: " + tokenResponse.getToken());
} catch (Exception e) {
System.out.println("Failed to retrieve client access token: " + e.getMessage());
}
return keycloak;
}
public ResponseEntity<String> registerUser(User user) {
// Using the client Keycloak instance for user registration
Keycloak keycloak = getClientKeycloak();
System.out.println("Creating user with email: " + user.getEmail());
// Set up the user representation for Keycloak
UserRepresentation newUser = new UserRepresentation();
newUser.setUsername(user.getUsername());
newUser.setEmail(user.getEmail());
newUser.setFirstName(user.getFirstName());
newUser.setLastName(user.getLastName());
newUser.setEnabled(true);
// Access target realm
RealmResource realmResource = keycloak.realm(environment.getProperty("keycloak.target-realm"));
UsersResource usersResource = realmResource.users();
// Create user in Keycloak
Response response = usersResource.create(newUser);
if (response.getStatus() != 201) {
System.out.println("Response status: " + response.getStatus());
System.out.println("Response: " + response.readEntity(String.class));
return new ResponseEntity<>("An error occurred while creating the user", HttpStatus.BAD_REQUEST);
}
String userId = response.getLocation().getPath().replaceAll(".*/([^/]+)$", "$1");
System.out.println("User created with ID: " + userId);
response.close();
// Set credentials
CredentialRepresentation passwordCred = new CredentialRepresentation();
passwordCred.setTemporary(false);
passwordCred.setType(CredentialRepresentation.PASSWORD);
passwordCred.setValue(user.getPassword());
UserResource userResource = usersResource.get(userId);
userResource.resetPassword(passwordCred);
return new ResponseEntity<>("User registration completed successfully.", HttpStatus.OK);
}
}
application.properties
keycloak.server-url=http://localhost:8080
keycloak.target-realm=my-realm
keycloak.client-id=my-client
keycloak.client-secret=hzkBBj8MY8hDaqHblrSkKbaDQvUnhbyb