-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Description
Describe the bug
When using the Delegation-based Strategy with OidcUserService as documented at https://docs.spring.io/spring-security/reference/6.0/servlet/oauth2/login/advanced.html#oauth2login-advanced-map-authorities-oauth2userservice, and the spring.security.oauth2.client.provider.<provider>.user-name-attribute is set, the username retrieved by SecurityContextHolder.getContext().getAuthentication().getName() is wrong.
This is because DefaultOidcUser has the following constructors:
DefaultOidcUser(Collection<? extends GrantedAuthority> authorities, OidcIdToken idToken, OidcUserInfo userInfo)DefaultOidcUser(Collection<? extends GrantedAuthority> authorities, OidcIdToken idToken, OidcUserInfo userInfo, String nameAttributeKey)
where the first calls the second, with thenameAtrributeKeydefaulting tosub.
The authority mapping replaces the authorities, but copies over the existing idToken and userInfo. It is however unable to copy over the nameAttributeKey because the OidcUser interface does not have a getter for nameAttributeKey.
Perhaps a builder could be added to DefaultOidcUser that copies the values over from an existing OidcUser (including the nameAttributeKey). This avoids having to change the OidcUser interface.
To Reproduce
- Configure an OIDC client with
spring-boot-starter-oauth2-client. - Configure a custom
OAuth2UserServiceto map authorities. For the simplest example to reproduce this issue:
@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo
.oidcUserService(this.oidcUserService())
...
)
);
return http.build();
}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
OidcUser oidcUser = delegate.loadUser(userRequest);
return new DefaultOidcUser(oidcUser.getAuthorities(), oidcUser.getIdToken(), oidcUser.getUserInfo());
};
}
}- Configure
spring.security.oauth2.client.provider.<provider>.user-name-attribute, e.g.
spring:
security:
oauth2:
client:
provider:
keycloak:
user-name-attribute: preferred_username- Call
SecurityContextHolder.getContext().getAuthentication().getName().
Expected behavior
SecurityContextHolder.getContext().getAuthentication().getName() should return the username from the configured username attribute (e.g. preferred_username from the example given above).
Sample
WIP - Will provide one soon