In this project we are going to develop and integrate a custom rest api in keycloak server.
About Keycloak
Keycloak is an open source Identity and Access Management solution aimed at modern applications and services. It makes it easy to secure applications and services with little to no code. It provides with following features,
- Single-Sign On
- Identity Brokering and Social Login
- User Federation
- Client Adapters
- Admin Console
- Account Management Console
- Standard Protocols: OpenID Connect, OAuth 2.0, and SAML.
Keycloak setup
We are going to use docker container to run Keycloak along with postgreSQL. We have integrated PostgreSQL with Keycloak inside docker-compose.yml
file.
To setup the docker containers, run the following command.
docker-compose -f ./docker-compose.yml up -d
Now, your keycloak should run locally on 8090
port.
Keycloak setup and adding user
URL for keycloak http://localhost:8090/
Note: By default keycloak uses
Master
realm. As you can see we added user inDemo
realm. In order to create new realm, do as follow
Now lets create a maven project. I will use VS Code as IDE for this project.However, you can use any IDE of your choice. You can check this link to configure VS Code for Java development.
Project Structure
pom.xml file contains all the required dependencies. You can copy the dependencies in your project.
Create a model user class
UserDto.java
under models folder.Create a mapper class that will map keycloak's UserModel class object to UserDto class object. Use import org.keycloak.models.UserModel;
to import UserModel class.
Keycloak provides RealmResourceProvider and RealmResourceProviderFactory interfaces that are used to implement custom rest api.
First we create KeyCloakUserApiProvider class that implements RealmResourceProvider interface. We will then define our custome api method named
searchUsersByAttribute
.
@GET
@Path("users/search-by-attr")
@NoCache
@Produces({ MediaType.APPLICATION_JSON })
@Encoded
public List<UserDto> searchUsersByAttribute(@DefaultValue(defaultAttr) @QueryParam("attr") String attr,
@QueryParam("value") String value) {
return session.users().searchForUserByUserAttribute(attr, value, session.getContext().getRealm())
.stream().map(e -> userMapper.mapToUserDto(e)).collect(Collectors.toList());
}
The above method filters user list based on user attribute. Default filter attribute is merchant_id
.
KeyCloakUserApiProvider class
Use import org.keycloak.services.resource.RealmResourceProvider;
to import the interface.
Lets define KeyCloakUserApiProviderFactory class that implements RealmResourceProviderFactory.
KeyCloakUserApiProviderFactory class
Note: Factory instance will remain through out the lifecycle of keycloak server but KeyCloakUserApiProvider instance will be created at run time.
Register the KeyCloakUserApiProviderFactory class to keycloak by creating org.keycloak.services.resource.RealmResourceProviderFactory
file under src\main\resources\META-INF\services\
folder.
Next copy the KeyCloakUserApiProviderFactory class name including package information into that file. For an example,
After that, build the maven project by running
mvn clean install
. This will generate a target
folder. Under the target folder there will be {project artifact id}-*.jar
file.
Copy that jar file to the Keycloak's standalone/deployments/
directory. For an example, If you run your Keycloak in docker container, you can use the following command:
docker cp <jar_file_path> keycloak:/opt/jboss/keycloak/standalone/deployments/
Test our custom api
Get list of users with merchant_id 1
curl --location --request GET 'http://localhost:8090/auth/realms/demo/userapi-rest/users/search-by-attr?attr=merchant_id&value=1'
You can find the project on this GitHub repository.
If you find this article useful, kindly give a start on GitHub.
Top comments (8)
I was modified the docker-compose file. But it didn't work. Do You have any suggestion? Thanks
version: '3.9'
from: github.com/keycloak/keycloak-conta...
volumes:
postgres_data:
driver: local
services:
postgres:
image: postgres
container_name: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
pgadmin:
container_name: pgadmin
image: dpage/pgadmin4
environment:
# PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
PGADMIN_DEFAULT_EMAIL: hendi@yopmail.com
# PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:password}
PGADMIN_DEFAULT_PASSWORD: password
ports:
- "5050:80"
restart: unless-stopped
keycloak:
image: jboss/keycloak:16.1.1
container_name: keycloak
environment:
DB_VENDOR: POSTGRES
DB_ADDR: postgres
DB_DATABASE: keycloak
DB_USER: keycloak
DB_SCHEMA: public
DB_PASSWORD: password
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: Pa55w0rd
ports:
- 8090:8080
depends_on:
- postgres
Hi,
this sounds very interesting.
Does this only allow to search for users with the attribute "merchantId"
If I want to search for a different attribute I need to adjust the mapper, right?
Cheers.
Yes, you have to adjust the mapper
it is easy to use this featurs with spring boot ?
Hi
Thanks for this post.
How to add authentication for the custom REST API?
I found this github.com/keycloak/keycloak/blob/...
Hi Mohammad, excelent article!
It's possible to implement the search without exact values ? for example value = '12' returns '123', '12x', ....
Is it possible to achieve this for Groups and Roles?
Search for Groups and Roles via custom attributes