Make OAuth2UserService Generic using OAuth2AuthorizedClient and OAuth2User types
Fixes gh-4706
This commit is contained in:
parent
3b80b6ded8
commit
8527daa22a
|
@ -20,28 +20,31 @@ import org.springframework.core.ResolvableType;
|
|||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
|
||||
import org.springframework.security.oauth2.client.authentication.NimbusAuthorizationCodeTokenExchanger;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.endpoint.AuthorizationRequestUriBuilder;
|
||||
import org.springframework.security.oauth2.client.jwt.JwtDecoderRegistry;
|
||||
import org.springframework.security.oauth2.client.jwt.NimbusJwtDecoderRegistry;
|
||||
import org.springframework.security.oauth2.client.userinfo.CustomUserTypesOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.DelegatingOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.oidc.OidcAuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.token.OAuth2TokenRepository;
|
||||
import org.springframework.security.oauth2.client.userinfo.CustomUserTypesOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.DelegatingOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRedirectFilter;
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
|
||||
import org.springframework.security.oauth2.core.AccessToken;
|
||||
import org.springframework.security.oauth2.client.endpoint.AuthorizationRequestUriBuilder;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
@ -188,14 +191,14 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
|||
}
|
||||
|
||||
public class UserInfoEndpointConfig {
|
||||
private OAuth2UserService userService;
|
||||
private OAuth2UserService<OAuth2AuthorizedClient, OAuth2User> userService;
|
||||
private Map<String, Class<? extends OAuth2User>> customUserTypes = new HashMap<>();
|
||||
private GrantedAuthoritiesMapper userAuthoritiesMapper;
|
||||
|
||||
private UserInfoEndpointConfig() {
|
||||
}
|
||||
|
||||
public UserInfoEndpointConfig userService(OAuth2UserService userService) {
|
||||
public UserInfoEndpointConfig userService(OAuth2UserService<OAuth2AuthorizedClient, OAuth2User> userService) {
|
||||
Assert.notNull(userService, "userService cannot be null");
|
||||
this.userService = userService;
|
||||
return this;
|
||||
|
@ -229,13 +232,13 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
|||
authorizationCodeTokenExchanger = new NimbusAuthorizationCodeTokenExchanger();
|
||||
}
|
||||
|
||||
OAuth2UserService oauth2UserService = this.userInfoEndpointConfig.userService;
|
||||
OAuth2UserService<OAuth2AuthorizedClient, OAuth2User> oauth2UserService = this.userInfoEndpointConfig.userService;
|
||||
if (oauth2UserService == null) {
|
||||
if (!this.userInfoEndpointConfig.customUserTypes.isEmpty()) {
|
||||
List<OAuth2UserService> userServices = new ArrayList<>();
|
||||
List<OAuth2UserService<OAuth2AuthorizedClient, OAuth2User>> userServices = new ArrayList<>();
|
||||
userServices.add(new CustomUserTypesOAuth2UserService(this.userInfoEndpointConfig.customUserTypes));
|
||||
userServices.add(new DefaultOAuth2UserService());
|
||||
oauth2UserService = new DelegatingOAuth2UserService(userServices);
|
||||
oauth2UserService = new DelegatingOAuth2UserService<>(userServices);
|
||||
} else {
|
||||
oauth2UserService = new DefaultOAuth2UserService();
|
||||
}
|
||||
|
@ -254,11 +257,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
|||
}
|
||||
http.authenticationProvider(this.postProcess(oauth2LoginAuthenticationProvider));
|
||||
|
||||
OAuth2UserService oidcUserService = this.userInfoEndpointConfig.userService;
|
||||
if (oidcUserService == null) {
|
||||
oidcUserService = new OidcUserService();
|
||||
}
|
||||
|
||||
OAuth2UserService<OidcAuthorizedClient, OidcUser> oidcUserService = new OidcUserService();
|
||||
OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider =
|
||||
new OidcAuthorizationCodeAuthenticationProvider(
|
||||
authorizationCodeTokenExchanger, oidcUserService, jwtDecoderRegistry);
|
||||
|
|
|
@ -22,8 +22,8 @@ import org.springframework.util.Assert;
|
|||
/**
|
||||
* A representation of an OAuth 2.0 <i>"Authorized Client"</i>.
|
||||
* <p>
|
||||
* A client is considered <i>"authorized"</i>
|
||||
* when it receives a successful response from the <i>Token Endpoint</i>.
|
||||
* A client is considered <i>"authorized"</i> when the End-User (Resource Owner)
|
||||
* grants authorization to the Client to access its protected resources.
|
||||
* <p>
|
||||
* This class associates the {@link #getClientRegistration() Client}
|
||||
* to the {@link #getAccessToken() Access Token}
|
||||
|
@ -35,12 +35,12 @@ import org.springframework.util.Assert;
|
|||
* @see AccessToken
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-5.1">Section 5.1 Access Token Response</a>
|
||||
*/
|
||||
public class AuthorizedClient {
|
||||
public class OAuth2AuthorizedClient {
|
||||
private final ClientRegistration clientRegistration;
|
||||
private final String principalName;
|
||||
private final AccessToken accessToken;
|
||||
|
||||
public AuthorizedClient(ClientRegistration clientRegistration, String principalName, AccessToken accessToken) {
|
||||
public OAuth2AuthorizedClient(ClientRegistration clientRegistration, String principalName, AccessToken accessToken) {
|
||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||
Assert.hasText(principalName, "principalName cannot be empty");
|
||||
Assert.notNull(accessToken, "accessToken cannot be null");
|
|
@ -19,7 +19,7 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -29,21 +29,25 @@ import java.util.Collection;
|
|||
* An implementation of an {@link AbstractAuthenticationToken}
|
||||
* that represents an <i>OAuth 2.0</i> {@link Authentication}.
|
||||
* <p>
|
||||
* This {@link Authentication} associates an {@link OAuth2User} principal
|
||||
* to an {@link AuthorizedClient}.
|
||||
* This {@link Authentication} associates an {@link OAuth2User} <code>Principal</code>
|
||||
* to an {@link OAuth2AuthorizedClient}, which the End-User (Principal) granted authorization to
|
||||
* so that it can access its protected resource(s) at the <i>UserInfo Endpoint</i>.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see AbstractAuthenticationToken
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2User
|
||||
* @see AuthorizedClient
|
||||
*
|
||||
* @param <U> The type of <i>OAuth 2.0 User</i>
|
||||
* @param <C> The type of <i>Authorized Client</i>
|
||||
*/
|
||||
public class OAuth2AuthenticationToken extends AbstractAuthenticationToken {
|
||||
public class OAuth2AuthenticationToken<U extends OAuth2User, C extends OAuth2AuthorizedClient> extends AbstractAuthenticationToken {
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
private final OAuth2User principal;
|
||||
private final AuthorizedClient authorizedClient;
|
||||
private final U principal;
|
||||
private final C authorizedClient;
|
||||
|
||||
public OAuth2AuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,
|
||||
AuthorizedClient authorizedClient) {
|
||||
public OAuth2AuthenticationToken(U principal, Collection<? extends GrantedAuthority> authorities, C authorizedClient) {
|
||||
super(authorities);
|
||||
Assert.notNull(principal, "principal cannot be null");
|
||||
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
|
||||
|
@ -53,7 +57,7 @@ public class OAuth2AuthenticationToken extends AbstractAuthenticationToken {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
public U getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
|
@ -63,7 +67,7 @@ public class OAuth2AuthenticationToken extends AbstractAuthenticationToken {
|
|||
return "";
|
||||
}
|
||||
|
||||
public AuthorizedClient getAuthorizedClient() {
|
||||
public C getAuthorizedClient() {
|
||||
return this.authorizedClient;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.core.AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
|
@ -41,7 +41,7 @@ import java.util.Collection;
|
|||
* an <i>authorization code</i> credential with the authorization server's <i>Token Endpoint</i>
|
||||
* and if valid, exchanging it for an <i>access token</i> credential.
|
||||
* <p>
|
||||
* It will also obtain the user attributes of the <i>End-User</i> (resource owner)
|
||||
* It will also obtain the user attributes of the <i>End-User</i> (Resource Owner)
|
||||
* from the <i>UserInfo Endpoint</i> using an {@link OAuth2UserService}
|
||||
* which will create a <code>Principal</code> in the form of an {@link OAuth2User}.
|
||||
*
|
||||
|
@ -49,8 +49,8 @@ import java.util.Collection;
|
|||
* @since 5.0
|
||||
* @see AuthorizationCodeAuthenticationToken
|
||||
* @see OAuth2AuthenticationToken
|
||||
* @see AuthorizedClient
|
||||
* @see OAuth2UserService
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2User
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.3">Section 4.1.3 Access Token Request</a>
|
||||
|
@ -60,12 +60,12 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
|
||||
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
|
||||
private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
|
||||
private final OAuth2UserService userService;
|
||||
private final OAuth2UserService<OAuth2AuthorizedClient, OAuth2User> userService;
|
||||
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
|
||||
|
||||
public OAuth2LoginAuthenticationProvider(
|
||||
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger,
|
||||
OAuth2UserService userService) {
|
||||
OAuth2UserService<OAuth2AuthorizedClient, OAuth2User> userService) {
|
||||
|
||||
Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
|
||||
Assert.notNull(userService, "userService cannot be null");
|
||||
|
@ -115,20 +115,20 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||
tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(),
|
||||
tokenResponse.getExpiresAt(), tokenResponse.getScopes());
|
||||
|
||||
AuthorizedClient authorizedClient = new AuthorizedClient(
|
||||
OAuth2AuthorizedClient oauth2AuthorizedClient = new OAuth2AuthorizedClient(
|
||||
authorizationCodeAuthentication.getClientRegistration(), "unknown", accessToken);
|
||||
|
||||
OAuth2User oauth2User = this.userService.loadUser(authorizedClient);
|
||||
OAuth2User oauth2User = this.userService.loadUser(oauth2AuthorizedClient);
|
||||
|
||||
// Update AuthorizedClient now that we know the 'principalName'
|
||||
authorizedClient = new AuthorizedClient(
|
||||
// Update OAuth2AuthorizedClient now that we know the 'principalName'
|
||||
oauth2AuthorizedClient = new OAuth2AuthorizedClient(
|
||||
authorizationCodeAuthentication.getClientRegistration(), oauth2User.getName(), accessToken);
|
||||
|
||||
Collection<? extends GrantedAuthority> mappedAuthorities =
|
||||
this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
|
||||
|
||||
OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(
|
||||
oauth2User, mappedAuthorities, authorizedClient);
|
||||
OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> authenticationResult =
|
||||
new OAuth2AuthenticationToken<>(oauth2User, mappedAuthorities, oauth2AuthorizedClient);
|
||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.client.oidc;
|
||||
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.AccessToken;
|
||||
import org.springframework.security.oauth2.core.oidc.IdToken;
|
||||
|
@ -24,8 +24,8 @@ import org.springframework.util.Assert;
|
|||
/**
|
||||
* A representation of an OpenID Connect 1.0 <i>"Authorized Client"</i>.
|
||||
* <p>
|
||||
* A client is considered <i>"authorized"</i>
|
||||
* when it receives a successful response from the <i>Token Endpoint</i>.
|
||||
* A client is considered <i>"authorized"</i> when the End-User (Resource Owner)
|
||||
* grants authorization to the Client to access its protected resources.
|
||||
* <p>
|
||||
* This class associates the {@link #getClientRegistration() Client}
|
||||
* to the {@link #getAccessToken() Access Token}
|
||||
|
@ -34,11 +34,11 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see IdToken
|
||||
* @see AuthorizedClient
|
||||
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse">3.1.3.3 Successful Token Response</a>
|
||||
*/
|
||||
public class OidcAuthorizedClient extends AuthorizedClient {
|
||||
public class OidcAuthorizedClient extends OAuth2AuthorizedClient {
|
||||
private final IdToken idToken;
|
||||
|
||||
public OidcAuthorizedClient(ClientRegistration clientRegistration, String principalName,
|
||||
|
|
|
@ -38,7 +38,6 @@ import org.springframework.security.oauth2.core.oidc.IdToken;
|
|||
import org.springframework.security.oauth2.core.oidc.OidcScope;
|
||||
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameter;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -65,8 +64,8 @@ import java.util.List;
|
|||
* @since 5.0
|
||||
* @see AuthorizationCodeAuthenticationToken
|
||||
* @see OAuth2AuthenticationToken
|
||||
* @see OidcAuthorizedClient
|
||||
* @see OidcUserService
|
||||
* @see OidcAuthorizedClient
|
||||
* @see OidcUser
|
||||
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">Section 3.1 Authorization Code Grant Flow</a>
|
||||
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenRequest">Section 3.1.3.1 Token Request</a>
|
||||
|
@ -77,13 +76,13 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
|
||||
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
|
||||
private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
|
||||
private final OAuth2UserService userService;
|
||||
private final OAuth2UserService<OidcAuthorizedClient, OidcUser> userService;
|
||||
private final JwtDecoderRegistry jwtDecoderRegistry;
|
||||
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
|
||||
|
||||
public OidcAuthorizationCodeAuthenticationProvider(
|
||||
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger,
|
||||
OAuth2UserService userService,
|
||||
OAuth2UserService<OidcAuthorizedClient, OidcUser> userService,
|
||||
JwtDecoderRegistry jwtDecoderRegistry) {
|
||||
|
||||
Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
|
||||
|
@ -153,21 +152,21 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
|
||||
this.validateIdToken(idToken, clientRegistration);
|
||||
|
||||
OidcAuthorizedClient authorizedClient = new OidcAuthorizedClient(
|
||||
OidcAuthorizedClient oidcAuthorizedClient = new OidcAuthorizedClient(
|
||||
clientRegistration, idToken.getSubject(), accessToken, idToken);
|
||||
|
||||
OAuth2User oauth2User = this.userService.loadUser(authorizedClient);
|
||||
OidcUser oidcUser = this.userService.loadUser(oidcAuthorizedClient);
|
||||
|
||||
// Update AuthorizedClient as the 'principalName' may have changed
|
||||
// Update OidcAuthorizedClient as the 'principalName' may have changed
|
||||
// (the default IdToken.subject) from the result of userService.loadUser()
|
||||
authorizedClient = new OidcAuthorizedClient(
|
||||
clientRegistration, oauth2User.getName(), accessToken, idToken);
|
||||
oidcAuthorizedClient = new OidcAuthorizedClient(
|
||||
clientRegistration, oidcUser.getName(), accessToken, idToken);
|
||||
|
||||
Collection<? extends GrantedAuthority> mappedAuthorities =
|
||||
this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
|
||||
this.authoritiesMapper.mapAuthorities(oidcUser.getAuthorities());
|
||||
|
||||
OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(
|
||||
oauth2User, mappedAuthorities, authorizedClient);
|
||||
OAuth2AuthenticationToken<OidcUser, OidcAuthorizedClient> authenticationResult =
|
||||
new OAuth2AuthenticationToken<>(oidcUser, mappedAuthorities, oidcAuthorizedClient);
|
||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package org.springframework.security.oauth2.client.oidc.userinfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.oidc.OidcAuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.userinfo.NimbusUserInfoRetriever;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
|
@ -27,8 +26,8 @@ import org.springframework.security.oauth2.core.OAuth2Error;
|
|||
import org.springframework.security.oauth2.core.oidc.OidcScope;
|
||||
import org.springframework.security.oauth2.core.oidc.UserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
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;
|
||||
|
||||
|
@ -48,23 +47,22 @@ import java.util.Set;
|
|||
* @since 5.0
|
||||
* @see OAuth2UserService
|
||||
* @see OidcAuthorizedClient
|
||||
* @see OidcUser
|
||||
* @see DefaultOidcUser
|
||||
* @see UserInfo
|
||||
* @see UserInfoRetriever
|
||||
*/
|
||||
public class OidcUserService implements OAuth2UserService {
|
||||
public class OidcUserService implements OAuth2UserService<OidcAuthorizedClient, OidcUser> {
|
||||
private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response";
|
||||
private UserInfoRetriever userInfoRetriever = new NimbusUserInfoRetriever();
|
||||
private final Set<String> userInfoScopes = new HashSet<>(
|
||||
Arrays.asList(OidcScope.PROFILE, OidcScope.EMAIL, OidcScope.ADDRESS, OidcScope.PHONE));
|
||||
|
||||
@Override
|
||||
public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException {
|
||||
OidcAuthorizedClient oidcAuthorizedClient = (OidcAuthorizedClient)authorizedClient;
|
||||
|
||||
public OidcUser loadUser(OidcAuthorizedClient authorizedClient) throws OAuth2AuthenticationException {
|
||||
UserInfo userInfo = null;
|
||||
if (this.shouldRetrieveUserInfo(oidcAuthorizedClient)) {
|
||||
Map<String, Object> userAttributes = this.userInfoRetriever.retrieve(oidcAuthorizedClient, Map.class);
|
||||
if (this.shouldRetrieveUserInfo(authorizedClient)) {
|
||||
Map<String, Object> userAttributes = this.userInfoRetriever.retrieve(authorizedClient, Map.class);
|
||||
userInfo = new UserInfo(userAttributes);
|
||||
|
||||
// http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
|
||||
|
@ -74,17 +72,17 @@ public class OidcUserService implements OAuth2UserService {
|
|||
// The sub Claim in the UserInfo Response MUST be verified to exactly match
|
||||
// the sub Claim in the ID Token; if they do not match,
|
||||
// the UserInfo Response values MUST NOT be used.
|
||||
if (!userInfo.getSubject().equals(oidcAuthorizedClient.getIdToken().getSubject())) {
|
||||
if (!userInfo.getSubject().equals(authorizedClient.getIdToken().getSubject())) {
|
||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);
|
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
GrantedAuthority authority = new OidcUserAuthority(oidcAuthorizedClient.getIdToken(), userInfo);
|
||||
GrantedAuthority authority = new OidcUserAuthority(authorizedClient.getIdToken(), userInfo);
|
||||
Set<GrantedAuthority> authorities = new HashSet<>();
|
||||
authorities.add(authority);
|
||||
|
||||
return new DefaultOidcUser(authorities, oidcAuthorizedClient.getIdToken(), userInfo);
|
||||
return new DefaultOidcUser(authorities, authorizedClient.getIdToken(), userInfo);
|
||||
}
|
||||
|
||||
public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) {
|
||||
|
@ -92,9 +90,9 @@ public class OidcUserService implements OAuth2UserService {
|
|||
this.userInfoRetriever = userInfoRetriever;
|
||||
}
|
||||
|
||||
private boolean shouldRetrieveUserInfo(OidcAuthorizedClient oidcAuthorizedClient) {
|
||||
private boolean shouldRetrieveUserInfo(OidcAuthorizedClient authorizedClient) {
|
||||
// Auto-disabled if UserInfo Endpoint URI is not provided
|
||||
if (StringUtils.isEmpty(oidcAuthorizedClient.getClientRegistration().getProviderDetails()
|
||||
if (StringUtils.isEmpty(authorizedClient.getClientRegistration().getProviderDetails()
|
||||
.getUserInfoEndpoint().getUri())) {
|
||||
|
||||
return false;
|
||||
|
@ -107,10 +105,10 @@ public class OidcUserService implements OAuth2UserService {
|
|||
// the resulting Claims are returned in the ID Token.
|
||||
// The Authorization Code Grant Flow, which is response_type=code, results in an Access Token being issued.
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(
|
||||
oidcAuthorizedClient.getClientRegistration().getAuthorizationGrantType())) {
|
||||
authorizedClient.getClientRegistration().getAuthorizationGrantType())) {
|
||||
|
||||
// Return true if there is at least one match between the authorized scope(s) and UserInfo scope(s)
|
||||
return oidcAuthorizedClient.getAccessToken().getScopes().stream().anyMatch(userInfoScopes::contains);
|
||||
return authorizedClient.getAccessToken().getScopes().stream().anyMatch(userInfoScopes::contains);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.client.userinfo;
|
||||
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -32,15 +32,16 @@ import java.util.Map;
|
|||
* representing the <i>UserInfo Endpoint</i> address.
|
||||
* <p>
|
||||
* This implementation uses a {@link UserInfoRetriever} to obtain the user attributes
|
||||
* of the <i>End-User</i> (resource owner) from the <i>UserInfo Endpoint</i>.
|
||||
* of the <i>End-User</i> (Resource Owner) from the <i>UserInfo Endpoint</i>.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2UserService
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2User
|
||||
* @see UserInfoRetriever
|
||||
*/
|
||||
public class CustomUserTypesOAuth2UserService implements OAuth2UserService {
|
||||
public class CustomUserTypesOAuth2UserService implements OAuth2UserService<OAuth2AuthorizedClient, OAuth2User> {
|
||||
private final Map<String, Class<? extends OAuth2User>> customUserTypes;
|
||||
private UserInfoRetriever userInfoRetriever = new NimbusUserInfoRetriever();
|
||||
|
||||
|
@ -50,7 +51,7 @@ public class CustomUserTypesOAuth2UserService implements OAuth2UserService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException {
|
||||
public OAuth2User loadUser(OAuth2AuthorizedClient authorizedClient) throws OAuth2AuthenticationException {
|
||||
String userInfoUri = authorizedClient.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri();
|
||||
Class<? extends OAuth2User> customUserType;
|
||||
if ((customUserType = this.customUserTypes.get(userInfoUri)) == null) {
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
package org.springframework.security.oauth2.client.userinfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
|
@ -45,14 +45,16 @@ import java.util.Set;
|
|||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2UserService
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2User
|
||||
* @see DefaultOAuth2User
|
||||
* @see UserInfoRetriever
|
||||
*/
|
||||
public class DefaultOAuth2UserService implements OAuth2UserService {
|
||||
public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2AuthorizedClient, OAuth2User> {
|
||||
private UserInfoRetriever userInfoRetriever = new NimbusUserInfoRetriever();
|
||||
|
||||
@Override
|
||||
public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException {
|
||||
public OAuth2User loadUser(OAuth2AuthorizedClient authorizedClient) throws OAuth2AuthenticationException {
|
||||
String userNameAttributeName = authorizedClient.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
|
||||
if (!StringUtils.hasText(userNameAttributeName)) {
|
||||
throw new IllegalArgumentException(
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.client.userinfo;
|
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -30,29 +30,32 @@ import java.util.Objects;
|
|||
* to it's internal <code>List</code> of {@link OAuth2UserService}'s.
|
||||
* <p>
|
||||
* Each {@link OAuth2UserService} is given a chance to
|
||||
* {@link OAuth2UserService#loadUser(AuthorizedClient) load} an {@link OAuth2User}
|
||||
* {@link OAuth2UserService#loadUser(OAuth2AuthorizedClient) load} an {@link OAuth2User}
|
||||
* with the first <code>non-null</code> {@link OAuth2User} being returned.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2UserService
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2User
|
||||
*
|
||||
* @param <C> The type of <i>Authorized Client</i>
|
||||
* @param <U> The type of <i>OAuth 2.0 User</i>
|
||||
*/
|
||||
public class DelegatingOAuth2UserService implements OAuth2UserService {
|
||||
private final List<OAuth2UserService> userServices;
|
||||
public class DelegatingOAuth2UserService<C extends OAuth2AuthorizedClient, U extends OAuth2User> implements OAuth2UserService<C, U> {
|
||||
private final List<OAuth2UserService<C, U>> userServices;
|
||||
|
||||
public DelegatingOAuth2UserService(List<OAuth2UserService> userServices) {
|
||||
public DelegatingOAuth2UserService(List<OAuth2UserService<C, U>> userServices) {
|
||||
Assert.notEmpty(userServices, "userServices cannot be empty");
|
||||
this.userServices = Collections.unmodifiableList(new ArrayList<>(userServices));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException {
|
||||
OAuth2User oauth2User = this.userServices.stream()
|
||||
public U loadUser(C authorizedClient) throws OAuth2AuthenticationException {
|
||||
return this.userServices.stream()
|
||||
.map(userService -> userService.loadUser(authorizedClient))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
return oauth2User;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ import org.springframework.http.client.AbstractClientHttpResponse;
|
|||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class NimbusUserInfoRetriever implements UserInfoRetriever {
|
|||
private final HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
|
||||
|
||||
@Override
|
||||
public <T> T retrieve(AuthorizedClient authorizedClient, Class<T> returnType) throws OAuth2AuthenticationException {
|
||||
public <T> T retrieve(OAuth2AuthorizedClient authorizedClient, Class<T> returnType) throws OAuth2AuthenticationException {
|
||||
URI userInfoUri = URI.create(authorizedClient.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri());
|
||||
BearerAccessToken accessToken = new BearerAccessToken(authorizedClient.getAccessToken().getTokenValue());
|
||||
|
||||
|
|
|
@ -16,24 +16,28 @@
|
|||
package org.springframework.security.oauth2.client.userinfo;
|
||||
|
||||
import org.springframework.security.core.AuthenticatedPrincipal;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are responsible for obtaining the user attributes
|
||||
* of the <i>End-User</i> (resource owner) from the <i>UserInfo Endpoint</i>
|
||||
* using the provided {@link AuthorizedClient#getAccessToken()}
|
||||
* of the <i>End-User</i> (Resource Owner) from the <i>UserInfo Endpoint</i>
|
||||
* using the {@link OAuth2AuthorizedClient#getAccessToken() Access Token}
|
||||
* granted to the {@link OAuth2AuthorizedClient Authorized Client}
|
||||
* and returning an {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see AuthorizedClient
|
||||
* @see AuthenticatedPrincipal
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2User
|
||||
* @see AuthenticatedPrincipal
|
||||
*
|
||||
* @param <C> The type of <i>Authorized Client</i>
|
||||
* @param <U> The type of <i>OAuth 2.0 User</i>
|
||||
*/
|
||||
public interface OAuth2UserService {
|
||||
public interface OAuth2UserService<C extends OAuth2AuthorizedClient, U extends OAuth2User> {
|
||||
|
||||
OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException;
|
||||
U loadUser(C authorizedClient) throws OAuth2AuthenticationException;
|
||||
|
||||
}
|
||||
|
|
|
@ -15,22 +15,22 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.client.userinfo;
|
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
|
||||
/**
|
||||
* A strategy for retrieving the user attributes
|
||||
* of the <i>End-User</i> (resource owner) from the <i>UserInfo Endpoint</i>
|
||||
* using the provided {@link AuthorizedClient#getAccessToken()}.
|
||||
* of the <i>End-User</i> (Resource Owner) from the <i>UserInfo Endpoint</i>
|
||||
* using the provided {@link OAuth2AuthorizedClient#getAccessToken() Access Token}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
* @see AuthorizedClient
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2UserService
|
||||
*/
|
||||
public interface UserInfoRetriever {
|
||||
|
||||
<T> T retrieve(AuthorizedClient authorizedClient, Class<T> responseType) throws OAuth2AuthenticationException;
|
||||
<T> T retrieve(OAuth2AuthorizedClient authorizedClient, Class<T> responseType) throws OAuth2AuthenticationException;
|
||||
|
||||
}
|
||||
|
|
|
@ -18,21 +18,23 @@ package org.springframework.security.oauth2.client.web;
|
|||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository;
|
||||
import org.springframework.security.oauth2.client.token.OAuth2TokenRepository;
|
||||
import org.springframework.security.oauth2.core.AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCode;
|
||||
import org.springframework.security.oauth2.core.endpoint.AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.AuthorizationResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2Parameter;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -134,8 +136,8 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
|||
clientRegistration, new AuthorizationExchange(authorizationRequest, authorizationResponse));
|
||||
authorizationCodeAuthentication.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
|
||||
OAuth2AuthenticationToken oauth2Authentication =
|
||||
(OAuth2AuthenticationToken) this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
|
||||
OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> oauth2Authentication =
|
||||
(OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient>) this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
|
||||
|
||||
this.accessTokenRepository.saveToken(
|
||||
oauth2Authentication.getAuthorizedClient().getAccessToken(),
|
||||
|
|
|
@ -27,12 +27,12 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCode;
|
||||
import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2Parameter;
|
||||
|
@ -98,7 +98,7 @@ public class OAuth2LoginAuthenticationFilterTests {
|
|||
@Test
|
||||
public void doFilterWhenAuthorizationCodeSuccessResponseThenAuthenticationSuccessHandlerIsCalled() throws Exception {
|
||||
ClientRegistration clientRegistration = TestUtil.githubClientRegistration();
|
||||
AuthorizedClient authorizedClient = new AuthorizedClient(
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||
clientRegistration, "principal", mock(AccessToken.class));
|
||||
OAuth2AuthenticationToken userAuthentication = new OAuth2AuthenticationToken(
|
||||
mock(OAuth2User.class), AuthorityUtils.createAuthorityList("ROLE_USER"), authorizedClient);
|
||||
|
|
Loading…
Reference in New Issue