DEV Community

Mohammad Abu Musa RABIUL
Mohammad Abu Musa RABIUL

Posted on

Keycloak Custom Rest Api (Search by user attribute - Keycloak)

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
Enter fullscreen mode Exit fullscreen mode

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 in Demo realm. In order to create new realm, do as follow
image

image
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

image

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());
    }
Enter fullscreen mode Exit fullscreen mode

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.

image

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/
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

image

You can find the project on this GitHub repository.

If you find this article useful, kindly give a start on GitHub.

Top comments (8)

Collapse
 
hendisantika profile image
Hendi Santika

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

Collapse
 
belfhi profile image
Johannes Reppin

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.

Collapse
 
silentrobi profile image
Mohammad Abu Musa RABIUL

Yes, you have to adjust the mapper

Collapse
 
oaztech profile image
Oussama ANDALOUSSI

it is easy to use this featurs with spring boot ?

Collapse
 
arulrajnet profile image
arulraj.net

Hi

Thanks for this post.

How to add authentication for the custom REST API?

Collapse
 
arulrajnet profile image
arulraj.net
Collapse
 
ccamba profile image
ccamba • Edited

Hi Mohammad, excelent article!
It's possible to implement the search without exact values ? for example value = '12' returns '123', '12x', ....

Collapse
 
stoany09 profile image
Siyaphakama Sosibo

Is it possible to achieve this for Groups and Roles?

Search for Groups and Roles via custom attributes