Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webflux and keycloak using for method security level @Preauthorize custom claim in jwt instead of default scope

As per title I am setting up webflux config like this

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {
String jwkSetUri = "http://localhost:8080/auth/realms/demo/protocol/openid-connect/certs";
@Bean 
public ReactiveJwtDecoder jwtDecoder() {

return 
ReactiveJwtDecoders.fromIssuerLocation("http://localhost:8080/auth/realms/demo");
}
 @Bean

public SecurityWebFilterChain configure(ServerHttpSecurity http) {

     http.authorizeExchange()

           
            .oauth2Login()

            .and()
          .
            ...
             .oauth2ResourceServer()
             .jwt()

    .jwkSetUri(jwkSetUri)

            // .jwtDecoder(jwtDecoder())

             .jwtAuthenticationConverter(grantedAuthoritiesExtractor());
            return http.build();
}

@Bean

Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {

    GrantedAuthoritiesExtractor extractor = new GrantedAuthoritiesExtractor();

    return new ReactiveJwtAuthenticationConverterAdapter(extractor);

}
static class GrantedAuthoritiesExtractor

       extends  JwtAuthenticationConverter {


    public Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {

        Collection<?> authorities = (Collection<?>)

                jwt.getClaims().getOrDefault("roles", Collections.emptyList());


        return authorities.stream()

                .map(Object::toString)

                .map(SimpleGrantedAuthority::new)

                .collect(Collectors.toList());
    }
}

with the following application.yml

security:
   oauth2:

     client:
       provider:
         keycloak-local:
           issuer-uri: http://localhost:8080/auth/realms/demo
       registration:
         keycloak-local:
           client-id: some-id
           client-secret: ...

     resourceserver:
       jwt:
     
         issuer-uri: http://localhost:8080/auth/realms/demo
         jwk-set-uri: http://localhost:8080/auth/realms/demo/protocol/openid- 
         connect/certs

 

the token format is like that

"realm_access": {
"roles": [
 "offline_access",
  "uma_authorization"
  ]
   },
 "resource_access": {
 "someclient": {
  "roles": [
    "uma_protection"
   ]
  },
  "account": {
    "roles": [
    "manage-account",
    "manage-account-links",
    "view-profile"
  ]
   }
   },
   "scope": "profile  email ",
   "organizationId": "605b74813bd72c65a24a1704",
   "accountId": "605b74813bd72c65a24a1709",
   "email_verified": false,
   "roles": [
   "ROLE_ADMIN"    
     ],
   "preferred_username": "someUsername",
   "userId": "605b74813bd72c65a24a1706",
   "email": "email.com"
    }`


    
  

and of course the "roles" array is the one that I would like ideally to access(is the role that I define in my DB).

After that setup it seems that nothing has changed . So in my controller I tried to use the @Preauthorize annotation to access to some method but it keeps working with the scope claim.

@PreAuthorize("hasAuthority('SCOPE_email')")

Does anyone knows what I am missing? Please note that I refer to official documentation but also to this tutorial. for webflux.

Thanks

like image 963
spring00 Avatar asked Oct 24 '25 14:10

spring00


1 Answers

By default, Spring Security converts the items in the scope or scp claim and uses the SCOPE_ prefix. If your application is a pure OAuth2 Resource Server, you can change both conventions by defining a custom ReactiveJwtAuthenticationConverter bean.

For example, to export authorities from a roles scope and using the `` prefix (since ROLE_ is already part of your role name), you can define the following bean. Spring Security will pick it up automatically, you don't need to reference it from your SecurityWebFilterChain configuration.

@Bean
public ReactiveJwtAuthenticationConverter jwtAuthenticationConverter() {
    var jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
    jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
    jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");

    var jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(
            new ReactiveJwtGrantedAuthoritiesConverterAdapter(jwtGrantedAuthoritiesConverter));

    return jwtAuthenticationConverter;
}

Since your application is also configured as an OAuth2 Client (with oauth2Login()), then you need a different approach. You can either define a GrantedAuthoritiesMapper or an OAuth2UserService. Examples for both options can be found in the Spring Security documentation.

like image 117
Thomas Vitale Avatar answered Oct 26 '25 04:10

Thomas Vitale



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!