@ -18,9 +18,8 @@ package org.springframework.security.oauth2.client.oidc.userinfo;
@@ -18,9 +18,8 @@ package org.springframework.security.oauth2.client.oidc.userinfo;
import java.time.Instant ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.Map ;
import java.util.Set ;
import java.util.function.BiFunction ;
import java.util.function.Function ;
import java.util.function.Predicate ;
@ -28,7 +27,6 @@ import reactor.core.publisher.Mono;
@@ -28,7 +27,6 @@ import reactor.core.publisher.Mono;
import org.springframework.core.convert.TypeDescriptor ;
import org.springframework.core.convert.converter.Converter ;
import org.springframework.security.core.GrantedAuthority ;
import org.springframework.security.core.authority.SimpleGrantedAuthority ;
import org.springframework.security.oauth2.client.registration.ClientRegistration ;
import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService ;
@ -40,6 +38,7 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -40,6 +38,7 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error ;
import org.springframework.security.oauth2.core.converter.ClaimConversionService ;
import org.springframework.security.oauth2.core.converter.ClaimTypeConverter ;
import org.springframework.security.oauth2.core.oidc.OidcIdToken ;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo ;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames ;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser ;
@ -47,7 +46,6 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
@@ -47,7 +46,6 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority ;
import org.springframework.security.oauth2.core.user.OAuth2User ;
import org.springframework.util.Assert ;
import org.springframework.util.StringUtils ;
/ * *
* An implementation of an { @link ReactiveOAuth2UserService } that supports OpenID Connect
@ -75,6 +73,8 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
@@ -75,6 +73,8 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
private Predicate < OidcUserRequest > retrieveUserInfo = OidcUserRequestUtils : : shouldRetrieveUserInfo ;
private BiFunction < OidcUserRequest , OidcUserInfo , Mono < OidcUser > > oidcUserMapper = this : : getUser ;
/ * *
* Returns the default { @link Converter } ' s used for type conversion of claim values
* for an { @link OidcUserInfo } .
@ -103,29 +103,15 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
@@ -103,29 +103,15 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
Assert . notNull ( userRequest , "userRequest cannot be null" ) ;
// @formatter:off
return getUserInfo ( userRequest )
. map ( ( userInfo ) - >
new OidcUserAuthority ( userRequest . getIdToken ( ) , userInfo )
)
. defaultIfEmpty ( new OidcUserAuthority ( userRequest . getIdToken ( ) , null ) )
. map ( ( authority ) - > {
OidcUserInfo userInfo = authority . getUserInfo ( ) ;
Set < GrantedAuthority > authorities = new HashSet < > ( ) ;
authorities . add ( authority ) ;
OAuth2AccessToken token = userRequest . getAccessToken ( ) ;
for ( String scope : token . getScopes ( ) ) {
authorities . add ( new SimpleGrantedAuthority ( "SCOPE_" + scope ) ) ;
}
String userNameAttributeName = userRequest . getClientRegistration ( ) . getProviderDetails ( )
. getUserInfoEndpoint ( ) . getUserNameAttributeName ( ) ;
if ( StringUtils . hasText ( userNameAttributeName ) ) {
return new DefaultOidcUser ( authorities , userRequest . getIdToken ( ) , userInfo ,
userNameAttributeName ) ;
}
return new DefaultOidcUser ( authorities , userRequest . getIdToken ( ) , userInfo ) ;
} ) ;
. flatMap ( ( userInfo ) - > this . oidcUserMapper . apply ( userRequest , userInfo ) )
. switchIfEmpty ( Mono . defer ( ( ) - > this . oidcUserMapper . apply ( userRequest , null ) ) ) ;
// @formatter:on
}
private Mono < OidcUser > getUser ( OidcUserRequest userRequest , OidcUserInfo userInfo ) {
return Mono . just ( OidcUserRequestUtils . getUser ( userRequest , userInfo ) ) ;
}
private Mono < OidcUserInfo > getUserInfo ( OidcUserRequest userRequest ) {
if ( ! this . retrieveUserInfo . test ( userRequest ) ) {
return Mono . empty ( ) ;
@ -193,4 +179,60 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
@@ -193,4 +179,60 @@ public class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<
this . retrieveUserInfo = retrieveUserInfo ;
}
/ * *
* Sets the { @code BiFunction } used to map the { @link OidcUser user } from the
* { @link OidcUserRequest user request } and { @link OidcUserInfo user info } .
* < p >
* This is useful when you need to map the user or authorities from the access token
* itself . For example , when the authorization server provides authorization
* information in the access token payload you can do the following : < pre >
* & # 64 ; Bean
* public OidcReactiveOAuth2UserService oidcUserService ( ) {
* var userService = new OidcReactiveOAuth2UserService ( ) ;
* userService . setOidcUserMapper ( oidcUserMapper ( ) ) ;
* return userService ;
* }
*
* private static BiFunction & lt ; OidcUserRequest , OidcUserInfo , Mono & lt ; OidcUser & gt ; & gt ; oidcUserMapper ( ) {
* return ( userRequest , userInfo ) - > {
* var accessToken = userRequest . getAccessToken ( ) ;
* var grantedAuthorities = new HashSet & lt ; GrantedAuthority & gt ; ( ) ;
* // TODO: Map authorities from the access token
* var userNameAttributeName = "preferred_username" ;
* return Mono . just ( new DefaultOidcUser (
* grantedAuthorities ,
* userRequest . getIdToken ( ) ,
* userInfo ,
* userNameAttributeName
* ) ) ;
* } ;
* }
* < / pre >
* < p >
* Note that you can access the { @code userNameAttributeName } via the
* { @link ClientRegistration } as follows : < pre >
* var userNameAttributeName = userRequest . getClientRegistration ( )
* . getProviderDetails ( )
* . getUserInfoEndpoint ( )
* . getUserNameAttributeName ( ) ;
* < / pre >
* < p >
* By default , a { @link DefaultOidcUser } is created with authorities mapped as
* follows :
* < ul >
* < li > An { @link OidcUserAuthority } is created from the { @link OidcIdToken } and
* { @link OidcUserInfo } with an authority of { @code OIDC_USER } < / li >
* < li > Additional { @link SimpleGrantedAuthority authorities } are mapped from the
* { @link OAuth2AccessToken # getScopes ( ) access token scopes } with a prefix of
* { @code SCOPE_ } < / li >
* < / ul >
* @param oidcUserMapper the function used to map the { @link OidcUser } from the
* { @link OidcUserRequest } and { @link OidcUserInfo }
* @since 6 . 3
* /
public final void setOidcUserMapper ( BiFunction < OidcUserRequest , OidcUserInfo , Mono < OidcUser > > oidcUserMapper ) {
Assert . notNull ( oidcUserMapper , "oidcUserMapper cannot be null" ) ;
this . oidcUserMapper = oidcUserMapper ;
}
}