Configure Keycloak 21 with Spring Security 6 – Spring

by
Ali Hasan
cucumber-java spring spring-boot spring-security

Quick Fix: Steps:

  1. application.yml:
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://<KEYCLOAK_SERVER_IP>/realms/<YOUR_REALM_NAME>/protocol/openi…​
  1. Get Token from Keycloak:
curl --location '…​' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=<KEYCLOAK_CLIENT_ID>' \
--data-urlencode 'client_secret=<KEYCLOAK_CLIENT_SECRET>'
  1. Inspect the Token at https://jwt.io/

  2. WebSecurityConfig.java:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {

        httpSecurity
                .authorizeHttpRequests(registry -&gt; registry
                        .requestMatcher("/my-api/**").hasRole("MY_REALM_ROLE")
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2Configurer -&gt; oauth2Configurer.jwt(jwtConfigurer -&gt; jwtConfigurer.jwtAuthenticationConverter(jwt -&gt; {
                    Map<String, Collection<String>> realmAccess = jwt.getClaim("realm_access");
                    Collection<String> roles = realmAccess.get("roles");
                    …​
                    return new…​;
                })))
        ;

        return httpSecurity.build();
    }
}

Prerequisites:

  • Ensure proper Role Mappings for the User in Keycloak
  • Generate Access Token using Postman with Bearer Token Authorization

The Problem:

Configure Spring Security 6 to work with Keycloak 21. The provided code snippets from the user’s question show errors and incorrect configuration. Despite setting up the client and realms in Keycloak, Spring Security is not properly configured. The error encountered is an HTTP 403 Forbidden access error when accessing a REST API link after successful authentication through Keycloak. Resolve this issue by providing a correct configuration for Spring Security that integrates with Keycloak 21.

The Solutions:

Solution 1: Keycloak 21 Server and Spring Security 6 – Resource Server Setup

1. application.yml
Configure Spring Security to use Keycloak as an OAuth2 Resource Server:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://<KEYCLOAK_SERVER_IP>/realms/<YOUR_REALM_NAME>
          jwk-set-uri: http://<KEYCLOAK_SERVER_IP>/realms/<YOUR_REALM_NAME>/protocol/openid-connect/certs

2. Obtain JWT Token from Keycloak Server
Retrieve an access token from Keycloak using a curl request:

curl --location 'http://<KEYCLOAK_SERVER_IP>/realms/test/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=<YOUR_USER_NAME>' \
--data-urlencode 'password=<YOUR_USER_PASSWORD>' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=<KEYCLOAK_CLIENT_ID>' \
--data-urlencode 'client_secret=<KEYCLOAK_CLIENT_SECRET>' \
--data-urlencode 'scope=openid'

3. Inspect JWT Token
Use jwt.io to inspect the access token and identify the roles associated with it.

4. WebSecurityConfig.java
In your Spring Security configuration, define how Spring Security should extract roles from the JWT token:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {

        // Note: Please change '/mp-api/**' to your desired rest controller path.
        httpSecurity
                .authorizeHttpRequests(registry -> registry
                        .requestMatchers("/my-api/**").hasRole("MY_REALM_ROLE")
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2Configurer -> oauth2Configurer.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwt -> {
                    Map<String, Collection<String>> realmAccess = jwt.getClaim("realm_access");
                    Collection<String> roles = realmAccess.get("roles");
                    var grantedAuthorities = roles.stream()
                            .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                            .collect(Collectors.toList());
                    return new JwtAuthenticationToken(jwt, grantedAuthorities);
                })))
        ;

        return httpSecurity.build();
    }
}

5. Keycloak Role Mapping
Ensure appropriate role mappings for the user in Keycloak.

6. Postman Usage
Use Postman to access your REST API with the obtained bearer token.

Q&A

What is the configuration of resource server with roles mapping with Spring Boot 3.0?

Format the access token according to your requirement using jwtAuthenticationConverter and set up proper role mapping in Keycloak, then add role based authorization.

What is the required Spring security configuration with keycloak 21.0.1 and Spring Boot 3.0.4?

The configuration for Spring security to work with keycloak 21.0.1 is similar to the configuration of the previous version of Keycloak and Spring Boot. Include required dependencies and configure issuer URI and JWK set URI in YML config.

Can you provide the code snippet for Spring security 6.1 to work with Keycloak and Spring Boot 3.1?

.jwkSetUri(jwkSetUri) should be used to resolve the JwtDecoder error, configure role-based authorization using Spring Security and also include required dependencies.

Video Explanation:

The following video, titled "Spring Security : Keycloak Authorization in Spring Boot Part - 1 ...", provides additional insights and in-depth exploration related to the topics discussed in this post.

Play video

In this video, we will be setting up Keycloak authorization on a Spring Boot application. This is in continuation to my previous video on ...