6

I'm trying to use both realm and resource roles in a java application with spring-security and keycloak. Unfortunately, keycloak will only return one or the other depending on the value of :

keycloak.use-resource-role-mappings=true

You can still get both with custom code, but it messes up annotations such as @PreAuthorize or the spring-boot method .isUserInRole, which leads to ugly code.

Is there a way to override either the @PreAuthorize method or the JSON token Keycloak returns in order to use both realm and resource roles? Currently, my implementation of keyclaok use a custom method replacing the @PreAuthorize at the start of every method, and it isn't pretty.

Thank you in advance.

Viral
  • 935
  • 1
  • 9
  • 22
Taarawa Kijaenb
  • 131
  • 1
  • 5
  • 1
    What kind of custom code did you write that it messed up the annotations? You should be able to achieve what you want by writing a custom JwtAuthenticationConverter (https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverter.html) to populate the GrantedAuthority from both the realm and resource roles stored in the JWT – Alexander H Feb 10 '20 at 09:58
  • Sorry I wasn't very clear on this, the annotation are fine but they aren't usable as they contain only half the role, I'll try to override the Jwt in order to get a clean solution I'm also trying to use a bean in the pre authorize in the format of @PreAuthorize("@KeycloakAuthorize.hasKeycloakRole('role')") but no success on this end either so far – Taarawa Kijaenb Feb 10 '20 at 10:12
  • 1
    Ok then theJwtAuthenticationConverter is probably your best bet since you can simply access the JWT and populate the authorities however you like, see for example: https://stackoverflow.com/questions/58205510/spring-security-mapping-oauth2-claims-with-roles-to-secure-resource-server-endp – Alexander H Feb 10 '20 at 10:15
  • I'll try this and post the solution if it works thank you :) – Taarawa Kijaenb Feb 10 '20 at 10:22
  • 1
    Np, let me know if it works! :) – Alexander H Feb 10 '20 at 10:34

1 Answers1

7

Got it to work overriding the KeycloakAuthenticationProvider as I was using it in the SecurityConfig. Here's the code for the custom provider

public class CustomKeycloakAuthenticationProvider extends KeycloakAuthenticationProvider {
    private GrantedAuthoritiesMapper grantedAuthoritiesMapper;

    public void setGrantedAuthoritiesMapper(GrantedAuthoritiesMapper grantedAuthoritiesMapper) {
        this.grantedAuthoritiesMapper = grantedAuthoritiesMapper;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
        List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();

        for (String role : token.getAccount().getRoles()) {
            grantedAuthorities.add(new KeycloakRole(role));
        }

// ADDING THE MODIFICATION AND ENABLING ROLE FROM RESSOURCE

        for (String role : token.getAccount().getKeycloakSecurityContext().getToken().getResourceAccess("CustomApplication").getRoles()) {
            grantedAuthorities.add(new KeycloakRole(role));
        }
        return new KeycloakAuthenticationToken(token.getAccount(), token.isInteractive(), mapAuthorities(grantedAuthorities));
    }

    private Collection<? extends GrantedAuthority> mapAuthorities(
            Collection<? extends GrantedAuthority> authorities) {
        return grantedAuthoritiesMapper != null
            ? grantedAuthoritiesMapper.mapAuthorities(authorities)
            : authorities;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return KeycloakAuthenticationToken.class.isAssignableFrom(aClass);
    }


}

And the modifications in SecurityConfig

@Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        CustomKeycloakAuthenticationProvider keycloakAuthenticationProvider = CustomKeycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    private CustomKeycloakAuthenticationProvider CustomKeycloakAuthenticationProvider() {
        return new CustomKeycloakAuthenticationProvider();
    }

Thank you for the help Alexander !

Taarawa Kijaenb
  • 131
  • 1
  • 5