1

I use client_credentials grant, a service sends a bearer token to my application (the bearer token is for a keycloak client which has access to my application's role)

In Springboot2.x.x it was super easy I just used the Keycloak adapter like this:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().disable().authorizeRequests().antMatchers("/*").hasRole("some-role").anyRequest()
                .permitAll();
    }
}

application.yml:

keycloak:
  auth-server-url: http://test-keycloak/auth
  realm: MyRealm
  resource: MyService
  use-resource-role-mappings: 'true'

Now as Springboot3 dropped the Keycloak adapter and switched to Oauth2 it no longer works.

I struggle with this almost for days. I haven't found any working example or migration guide. The few I found is for authorization_code grant type or needs additional non-official dependencies like this: https://stackoverflow.com/a/74572732/5454794 which I want to avoid.

For Springboot3 I have something like this:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http,GrantedAuthoritiesMapper authoritiesMapper) throws Exception {
        http.csrf().disable().authorizeHttpRequests(request->request.
                requestMatchers("/*").hasRole("some-role")
                .anyRequest()
                .permitAll());
        return http.build();
    }
}

application.yml

  security:
    oauth2:
      client:
        registration:
          keycloak:
            provider: keycloak
            client-id: MyService
            authorization-grant-type: client_credentials
        provider:
          keycloak:
            issuer-uri: http://test-keycloak/auth/realms/MyRealm
            token-uri: http://test-keycloak/auth/realms/MyRealm/protocol/openid-connect/token

All I get is a 403 , I guess because of "use-resource-role-mappings: 'true'" is missing in the new configuration. But I totally don't know what to do... I saw a solution which put it under the resource like this:

  spring.security.oauth2.resource:
    user-info-uri: http://test-keycloak/auth/realms/MyRealm/protocol/openid-connect/userinfo
    use-resource-resource-mappings: true

no effect ,still get 403...

So I am totally lost what to do, it is just insane that there is no official migration guide for this...

beatrice
  • 3,684
  • 5
  • 22
  • 49

1 Answers1

0

The solution:

We want to use our app as a resource server:

application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: <keycloak-host>/auth/realms/my-realm

code:
And because i couldn't find any settings for the legacy resource and resource mapping I added a custom function to accomplish the same namely: 'jwtRoleConverter'

    @Configuration
    @EnableWebSecurity
    @EnableMethodSecurity
    public class SecurityConfig {
    
        @Bean
        protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
            return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
        }
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http,GrantedAuthoritiesMapper authoritiesMapper) throws Exception {
            http.csrf().disable().authorizeHttpRequests(request->request.
                    requestMatchers("/*").hasRole("some-role")
                    .anyRequest()
                    .permitAll());    
   http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtRoleConverter());
            return http.build();
        }

       public JwtAuthenticationConverter jwtRoleConverter() {
        Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = jwt -> {
            var rolesOpt = Optional.ofNullable(jwt.<Map<String, Map<String, Collection<String>>>>getClaim("resource_access"))
                    .map(ra -> ra.get(<resource_name>))
                    .map(r -> r.get("roles"));
            if (rolesOpt.isEmpty()) {
                // do something when no roles present
            }
            return rolesOpt.get().stream()
                    .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                    .collect(Collectors.toList());
        };

        var jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);

        return jwtAuthenticationConverter;
    }
    }
beatrice
  • 3,684
  • 5
  • 22
  • 49