Posted on Leave a comment

Integrate Keycloak With Spring Boot Step by Step

Recently, I had to configure an API to use Keycloak to authenticate and authorize users of a web application. After spending almost two weeks, I finally got the app up and running as required. If you are on the same boat, read on since I’ll provide step by step (with pictures!) to help you save time on this setup so you can spend more time with actual development.

API Requirements

There are some requirements as follow:

  • The spring boot API can create and login (return access token) to the caller
  • There are three roles (admin, moderator, member) in the app and each API endpoint can be guarded by a specific role require. There are also endpoints that don’t require authorization (publicly available)
  • Endpoints that are accessible by member are also accessible by admin and moderator. Endpoints that are accessible by moderator are accessible by admin

Code repository

If you are the impatient kind (like me), you can skip the post and go directly to the source code (available on Github) to start tinkering with the application here.

However, when you are stuck, feel free to go back to this post to find the missing pieces.

Quick Keycloak setup with docker compose

If you have a Keycloak instance up and running, you can skip this part. However, in case you haven’t, use the docker-compose file below to quickly set up Keycloak:

version: '3'

    container_name: keycloak
    image: jboss/keycloak:15.0.2
    restart: always
    env_file: ./keycloak.env
      - keycloak_db
      - ./realm.json:/tmp/realm.json
      - "18080:8080"

    container_name: keycloak_db
    image: mariadb:10.3.26
    restart: always
      - keycloak_db_vol:/var/lib/mysql
      - ./keycloak.env

The keycloak.env contains some environment variables:



Now run docker-compose up -d then you should be able to access keycloak at http://localhost:18080 in a few minutes (docker may need to pull the images from its registry, which depends largely on the speed of your connection).

For the lazy guys, please go to this location in the repo and hit docker-compose up -d

Once, you have access to Keycloak, login with the id and password specified in the environment file (kc_dev and kc_dev1231232).

In the beginning, there should be only one realm, you need to click on Add realm to create a new realm.

Create a new realm in keycloak
Create a new realm in keycloak

Now the keycloak instance is ready. It’s time to create and configure a client.

Keycloak client configuration

The client’s configuration is very important so you need to pay close attention to this part.

At the beginning, there are some default clients:

Keycloak's default clients

You can click on the Create button on the right to create a new client like this:

Create new keycloak client

And configure the client like this:

keycloak client's configuration

Now, it’s time to create roles for the client. As you can remember, we have three roles:

  • admin
  • moderator
  • member

From the requirement at the beginning, we know that admin and moderator are composite roles while member is not a composite role.

A composite role is a role that consists of one or
more other roles

Let’s switch to the Roles tab and click on Add role

add new role in keycloak

After creating the role, you will see there is a switch to turn that role into a composite role. For the member role, we don’t need that.

However, as mentioned above, admin and moderator are composite roles, we need to turn that option on:

adding composite role

As you can see in this case, when creating moderator role, I turned the composite role on. In the Client roles select box, I select this client (spring-boot-client).

Let’s do the same for the admin role.

You don’t need to add the member role to the associated roles box since moderator includes member already.

Spring boot application configuration

Let’s configure the file.

#use external in production

#name of the client

All the fields in this configuration file are self-explanatory. You may wonder where to get the secret? It’s in the Credentials tab of the client:

getting keycloak's client secrets

Also, for Keycloak to guard your endpoints, you need to create a configuration file like this:

package com.datmt.keycloak.springbootauth.config;

import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    protected void configure(HttpSecurity http) throws Exception {

    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());

    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());

    public KeycloakConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();

Here, you can see that I’ve configured that:

  • Paths begins with /admin requrie admin role
  • Paths begin with /member require member role
  • Paths begin with /moderator require moderator role
  • Paths begin with /public is accessible by all

Let’s create a controller and test our config:

package com.datmt.keycloak.springbootauth.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

public class HelloController {

	public ResponseEntity<String> helloPublic() {
		return ResponseEntity.ok("Hello public user");
	public ResponseEntity<String> helloMember() {
		return ResponseEntity.ok("Hello dear member");
	public ResponseEntity<String> helloModerator() {
		return ResponseEntity.ok("Hello Moderator");
	public ResponseEntity<String> helloAdmin() {
		return ResponseEntity.ok("Nice day, admin");


Let’s try the public endpoint:

unguarded endpoint
Public endpoint works

As you can see, it works as expected. The path begins with /public so anyone can access without any authentication.

Now let’s try the path for member:

and also admin:

You can see that since the request didn’t present any kind of credentials, the response is 401.

To make sure this works, let’s create users with specific roles and test if they can access the endpoints with his token.

Let’s first create an user with member role:

After creating the user, go to Role Mappings tab, select spring-boot-client and add member to user’s list of roles

Now, to quickly get the token for this user, go back to the details tab and click on Impersonate:

In the new tab that appears, open the network tab of the developer console, reload and copy the access token:

impersonate and get token

Now, use that token to make a request in postman:

You expected that to be a 200 response, right? But what I got was 403. That’s better than 401 since we can authenticate with the token but somehow, the user was not authorized to access the endpoint.

What’s the missing configuration?

If you decode the token using sites like, you will see that even though the user has role member, it didn’t show in the token:

It turned out, you also need to configure client scopes so the roles appear on the token.

Then go to Scopes and add all roles of the client spring-boot-client

Now, let’s impersonate the user again. Now, her roles should be visible on the token decoding page:

Let’s make the same request to them member’s endpoint again but with the new token, sure enough, it worked:

You can try with other roles, they should work too.

Guarding API with RolesAllowed

Guarding endpoint with path prefixes is OK and sometimes that’s a clean solution. However, there are times you may need a finer tune to your access policy.

In such case, you can use RolesAllowed to specify which role can access a specific endpoint, no matter what its path prefix is.

	public ResponseEntity<String> helloCustom() {
		return ResponseEntity.ok("Nice day, my custom user");

You can guess from the annotation, anyone with role member can access this endpoint.

What if the path is prefix but /public?

	public ResponseEntity<String> helloCustom() {
		return ResponseEntity.ok("Nice day, it appears to be public but not");

If you try to access the URL, even though the prefix is public, you will get a 401 without a token:

But if a user with member role, the access is granted:

overwrite path prefix with rolesallowed

Create account and login using Keycloak Admin Client

Now the API can talk to keycloak to authenticate and authorize users, let’s also integrate user login and creation to the API so we can get the token via the API.

Before doing anything with the code, let’s add some more configuration to the client so it has what it needs to manage user.

First, go to the client’s Service Account Role tab and configure as follow:

Configure role for the service account
Add manager users, query users role for the client’s service account

Now, the client is able to manage users.

Let’s add the keycloak-admin-client package to maven. You need this to enable spring boot to operate Keycloak’s admin-related tasks.


Next add a KeycloakProvider class:

public class KeycloakProvider {

    public String serverURL;
    public String realm;
    public String clientID;
    public String clientSecret;

    private static Keycloak keycloak = null;

    public KeycloakProvider() {

    public Keycloak getInstance() {
        if (keycloak == null) {

            return KeycloakBuilder.builder()
        return keycloak;

    public KeycloakBuilder newKeycloakBuilderWithPasswordCredentials(String username, String password) {
        return KeycloakBuilder.builder() //
                .realm(realm) //
                .clientId(clientID) //
                .clientSecret(clientSecret) //
                .username(username) //

    public JsonNode refreshToken(String refreshToken) throws UnirestException {
        String url = serverURL + "/realms/" + realm + "/protocol/openid-connect/token";
                .header("Content-Type", "application/x-www-form-urlencoded")
                .field("client_id", clientID)
                .field("client_secret", clientSecret)
                .field("refresh_token", refreshToken)
                .field("grant_type", "refresh_token")

Then a service to create and log in user:

public class KeycloakAdminClientService {
    public String realm;

    private final KeycloakProvider kcProvider;

    public KeycloakAdminClientService(KeycloakProvider keycloakProvider) {
        this.kcProvider = keycloakProvider;

    public Response createKeycloakUser(CreateUserRequest user) {
        UsersResource usersResource = kcProvider.getInstance().realm(realm).users();
        CredentialRepresentation credentialRepresentation = createPasswordCredentials(user.getPassword());

        UserRepresentation kcUser = new UserRepresentation();
        return usersResource.create(kcUser);


    private static CredentialRepresentation createPasswordCredentials(String password) {
        CredentialRepresentation passwordCredentials = new CredentialRepresentation();
        return passwordCredentials;


Finally, create a controller to create and login user:

public class UserController {
    private final KeycloakAdminClientService kcAdminClient;

    private final KeycloakProvider kcProvider;

    private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(UserController.class);

    public UserController(KeycloakAdminClientService kcAdminClient, KeycloakProvider kcProvider) {
        this.kcProvider = kcProvider;
        this.kcAdminClient = kcAdminClient;

    @PostMapping(value = "/create")
    public ResponseEntity<Response> createUser(@RequestBody CreateUserRequest user) {
        Response createdResponse = kcAdminClient.createKeycloakUser(user);
        return ResponseEntity.ok(createdResponse);


    public ResponseEntity<AccessTokenResponse> login(@NotNull @RequestBody LoginRequest loginRequest) {
        Keycloak keycloak = kcProvider.newKeycloakBuilderWithPasswordCredentials(loginRequest.getUsername(), loginRequest.getPassword()).build();

        AccessTokenResponse accessTokenResponse = null;
        try {
            accessTokenResponse = keycloak.tokenManager().getAccessToken();
            return ResponseEntity.status(HttpStatus.OK).body(accessTokenResponse);
        } catch (BadRequestException ex) {
            LOG.warn("invalid account. User probably hasn't verified email.", ex);
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(accessTokenResponse);



Too much code? No worries, they are all available on Github.

Now, open postman and call these endpoints. First, create a user:

If you login with that user now, you will get an error since the user hasn’t got email verified:

Simply switch email verified on then y ou can get the tokens:

Notice that you need to put email in the place of user name because, by default, email is used as username:

Obviously, you can turn this off to log in with the username.


In this super long post, I created a project that help you to quickly configure keycloak with your spring boot app. You can now use this project as a base and start creating your app’s business logic.

Code repo is here:

Posted on Leave a comment

Fix 403 Forbidden when creating users in Keycloak

Recently I try using Keycloak for IAM for my new website. I can log in just fine but couldn’t create any user.

I use a client to do all the tasks with Keycloak. After a few hours of searching, I found out that the client missed the manage-users role. That’s why it couldn’t create any user despite I have my Java code setup correctly.

So, what’s the fix?

The fix turned out to be very simple.

Here are the steps:

First, login to your realm, select your client and go to service account roles.

You’ll see something like this:

So, the magic is to select realm-management in the Client Roles select box and add user-management role to your service account.

That’s all you need to do to avoid 403 when creating users with Keycloak.

Posted on Leave a comment

Configure SSO Server With Keycloak, HAProxy & Docker

Keycloak is quite a nice tool to handle user authentication and authorization. Both Keycloak and HAProxy are free so you can easily setup an authenication & authorization server very quickly and free (hosting is not free though :)).

With the help of Docker, it will take a few minutes (less than 10) for you to successfully setup a single sign on server(SSO).

Setting up Keycloak

Keycloak has built in database to store users. However, it also allows you specify an external database if you want to do so. I prefer the second option since it seems easier to backup. Let’s first setup a mariadb server. Here is the docker-compose part of MariaDB:

    container_name: keycloak_db
    image: mariadb:10.3.26
    restart: always
      - keycloak_db_volume:/var/lib/mysql

Notice that I’m using MYSQL_ROOT_PASSWORD here for demo purposes. You should create a non root user and also user Docker secrets to manage the password instead.

You can also notice that this service (keycloak_db) uses an external volume keycloak_db_volume. We will create that at the end of the docker-compose.yml file.

Now, let’s write the YAML content for Keycloak itself:

    container_name: keycloak
    restart: always
    env_file: ./kc.env

As you can see, instead using environment block, we now use an *.env file. Here is the content:


That’s all we need to do with Keycloak. Let’s create and configure HAproxy.

Setting up HAproxy

If you want to have SSL enabled, make sure to install certbot to generate a free Let’s Encrypt certificate. Certbot is awesome since you can set up it to automatically renew the certificate for you.

There is a awesome tutorial here to help you generate standalone certificate for your domain:

After generating the certificate, combine the fullchain.pem and privkey.pem to generate a single .pem file. HAproxy will use this single file.

DOMAIN='your_domain_name' sudo -E bash -c 'cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/letsencrypt/live/$DOMAIN/privkey.pem > /etc/haproxy/certs/$DOMAIN.pem'

Now, let’s create a service for HAproxy in our docker-compose.yml file:

    container_name: haproxy
    image: haproxy:2.4.0
    restart: always
      - 80:80
      - 443:443
      - /etc/haproxy/certs/your_domain_name.pem:/usr/local/etc/haproxy/certs/your_domain_name.pem
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg

As you can see, we’ve mounted the SSL certificate to /usr/local/etc/haproxy/certs/. We also create and mount a haproxy.cfg file. Let’s see its content:

        stats timeout 30s

        log     global
        mode    http
        option  httplog
        option  dontlognull
  timeout connect 5000
  timeout client  50000
  timeout server  50000
        option forwardfor
        option http-server-close

frontend sso
        bind :80
        bind :443 ssl crt /usr/local/etc/haproxy/certs/your_domain_name.pem
  http-request redirect scheme https unless { ssl_fc }
        default_backend keycloak_backend
  http-request set-header X-Forwarded-Proto https if { ssl_fc }
  http-request set-header X-Forwarded-Proto http if !{ ssl_fc }

backend keycloak_backend
  http-request redirect scheme https unless { ssl_fc }
        server www-1 keycloak:8080 check

By default, Keycloak starts on port 8080. This is HAproxy settings, we forward traffic on port 80, 443 to Keycloak backend.

Here is the whole docker-compose file:

version: '3'

    container_name: haproxy
    image: haproxy:2.4.0
    restart: always
      - 80:80
      - 443:443
      - /etc/haproxy/certs/
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg

    container_name: keycloak
    restart: always
    env_file: ./kc.env
    container_name: keycloak_db
    image: mariadb:10.3.26
    restart: always
      - keycloak_db_volume:/var/lib/mysql
      MYSQL_ROOT_PASSWORD: mysql_root_password


If you have the certificate correctly setup, simply run docker-compose up -d, you can access your site after a few minutes.

Some caveats:

I’ve tried this setup on a DigitalOcean droplet with just 1GB of RAM and single CPU ($5/month) and Keycloak crashed every single time. The reason is at start up, Keycloak uses a lot of resources. Afte upgrading to the next tier, I could start without any problem.