Summary
I'm having a hard time discovering how to map claims of an OIDC logon to GrantedAuthorities. This doc seems to describe it for the non-reactive stack, but I can't find anything similar for WebFlux.
I also located a use of GrantedAuthoritiesMapper in org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAutheniticationProvider. Based on the interface and docs it seems like this is the SPI I'm meant to use for mapping authorities since it was utilized for this ticket. However, the value is initialized to a no-op and is only set via OAuth2LoginConfigurer which is part of the non-reactive stack.
Actual Behavior
No mechanism exposed to set GrantedAuthoritiesMapper for Webflux
Expected Behavior
A method on OAuth2LoginSpec to set GrantedAuthoritiesMapper.
Version
5.1.1
Comment From: wtatum
Sorry, it looks like the relevant mapper is in org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager. That property doesn't even have a setter, so I don't think I could override it even if I grab a reference to the bean.
Comment From: jgrandja
@wtatum I would recommend using a delegation-based strategy to map authorities as it's more flexible than the use of a GrantedAuthoritiesMapper. An example is provided for the Servlet side of things here.
You can use the same pattern for reactive but you will need to register a custom @Bean of type ReactiveOAuth2UserService<OidcUserRequest, OidcUser> that first delegates to OidcReactiveOAuth2UserService and than you can implement your custom authority mapping before returning a new Mono<OidcUser>.
Does this help?
Comment From: wtatum
That sounds straightforward enough. I'll give it a shot.
On Mon, Dec 10, 2018, 10:38 AM Joe Grandja <notifications@github.com wrote:
@wtatum https://github.com/wtatum I would recommend using a delegation-based strategy to map authorities as it's more flexible than the use of a GrantedAuthoritiesMapper. An example is provided for the Servlet side of things here https://docs.spring.io/spring-security/site/docs/5.1.2.RELEASE/reference/htmlsingle/#oauth2login-advanced-map-authorities-oauth2userservice .
You can use the same pattern for reactive but you will need to register a custom @Bean of type ReactiveOAuth2UserService
that first delegates to OidcReactiveOAuth2UserService and than you can implement your custom authority mapping before returning a new Mono . Does this help?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-security/issues/6266#issuecomment-445859562, or mute the thread https://github.com/notifications/unsubscribe-auth/AHOStVszpVu2DhB1qlW2uhUgFLAI1BlMks5u3oARgaJpZM4ZLbtn .
Comment From: jgrandja
@wtatum Did you manage to get this to work based on the solution I provided? If so, can you please close out this issue.
Comment From: jgrandja
@wtatum I'm going to close this issue based on the assumption that you have things working. We can re-open if your require further assistance.
Comment From: codependent
In case someone ends up here looking for a solution, what @jgrandja suggested works great, e.g.:
@Bean
fun oidcUserService(): ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {
val delegate = OidcReactiveOAuth2UserService()
return ReactiveOAuth2UserService { userRequest ->
val oidcUser = delegate.loadUser(userRequest)
.map {oidcUser ->
val accessToken = userRequest.accessToken
val mappedAuthorities = mutableSetOf<GrantedAuthority>()
mappedAuthorities.addAll(oidcUser.authorities)
permissionService.loadPermissions(oidcUser.preferredUsername).forEach {
mappedAuthorities.add(SimpleGrantedAuthority("ROLE_$it"))
}
DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo) as OidcUser
}
oidcUser
}
}
Comment From: henrik-larsson-15-wcar
What if im only interested in the custom claims from the access token and i dont want to use the user endpoint. The above implementation assumes I want to use the userinfo endpoint.
Comment From: mcruzdev
Hi, I am trying to map claims to roles a two weeks
I created the @Bean mentioned by @mraible but it is not be invoked:
@Bean
public ReactiveOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcReactiveOAuth2UserService delegate = new OidcReactiveOAuth2UserService();
return (userRequest) -> {
// Delegate to the default implementation for loading a user
return delegate.loadUser(userRequest)
.doOnError(err -> log.info("Err: ", err.getCause()))
.doOnSuccess(user -> log.info("Auths " + user.getAuthorities()))
.map(user -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
user.getAuthorities().forEach(authority -> {
if (authority instanceof OidcUserAuthority) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
mappedAuthorities.addAll(extractAuthorityFromClaims(oidcUserAuthority.getUserInfo().getClaims()));
}
});
return new DefaultOidcUser(mappedAuthorities, user.getIdToken(), user.getUserInfo());
});
};
}
I need other configuration? I am using Spring Cloud Gateway and OAuth2 SpringBoot version: 2.3.3.RELEASE
Comment From: jzheaux
@mcruzdev sorry you are having trouble. StackOverflow is typically a better forum for support questions. Would you consider updating your comment with a SO link, and I'd be happy to take a look?
Comment From: jzheaux
@henrik-larsson-15-wcar, it's atypical for a client to read the claims of an access token since those are intended for granting access to resource servers. You might be referring to the id token, though.
If it's an id token, that's parsed for you and included in the OidcUserRequest, so a custom instance of ReactiveOAuth2UserService will still suffice - it just won't delegate to OidcReactiveOAuth2UserService.
If it is an access token you are referring to, your custom implementation would first need to parse and verify the token, which you could do with NimbusJwtDecoder.
If you have more questions, feel free to post a question to StackOverflow and add the link here. I'd be happy to help further over on SO.