@RolesAllowed annotation always throw 403 error with Keycloak

Viewed 1306

I'm a new one in Spring boot and have maybe a stupid question. I have a simple spring boot rest api application with spring security and oauth2. Outh2 broker is Keycloak So my security filter looks like this

@Configuration
@EnableWebSecurity
public class WebSecurityAdditionalConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors()
                .and()
                .authorizeRequests()
                .antMatchers("/api/**")
                .authenticated()
                .and()
                .oauth2ResourceServer().jwt();
        http.csrf().disable().sessionManagement().sessionCreationPolicy(
                SessionCreationPolicy.STATELESS);
    }

}

Also I enable global method security


@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true, securedEnabled = true, prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}

But when I try to add @RolesAllowed('admin') to my controller method I always get 403 forbidden error Without annotations all works fine, without token I get 401 and if token expired 403. This is example of my jwt

{
  
  "realm_access": {
    "roles": [
      "admin"
    ]
  },
  "resource_access": {
    "edge_client": {
      "roles": [
        "cli_admin"
      ]
    }
  },
  "scope": "profile web-origins email",
  "email_verified": false,
  "name": "John Spartan",
  "groups": [
    "admin"
  ],
  "preferred_username": "test_admin",
  "given_name": "John",
  "family_name": "Spartan"
}
1 Answers

I think this class is responsible for getting authorities. By default it is looking for scope or scp claims in jwt. In your case you have "scope": "profile web-origins email". After that it prefixes every authority with DEFAULT_AUTHORITY_PREFIX equals to SCOPE_. I think when you will debug your Authentication object from SecurityContextHolder.getContext().getAuthentication() authorities returned by its getAuthorities() will be equals to SCOPE_profile, SCOPE_web-origins and SCOPE_email. You should change your code to:

.oauth2ResourceServer()
.jwt(customizer -> {
    JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
    //write your own Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter 
    //and override Collection<GrantedAuthority> convert(Jwt jwt) to get roles from
    //realm_access.roles
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
    customizer.jwtAuthenticationConverter(jwtAuthenticationConverter)
})

or use Keycloak Adapter for Spring instead of oauth2ResourceServer()

Related