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.
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 !
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With