Jelajahi Sumber

Make OAuth2UserService Generic using OAuth2AuthorizedClient and OAuth2User types

Fixes gh-4706
Joe Grandja 7 tahun lalu
induk
melakukan
8527daa22a
15 mengubah file dengan 125 tambahan dan 113 penghapusan
  1. 16 17
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java
  2. 4 4
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClient.java
  3. 15 11
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java
  4. 11 11
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java
  5. 6 6
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/OidcAuthorizedClient.java
  6. 11 12
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java
  7. 13 15
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java
  8. 5 4
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/CustomUserTypesOAuth2UserService.java
  9. 6 4
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java
  10. 11 8
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DelegatingOAuth2UserService.java
  11. 2 2
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/NimbusUserInfoRetriever.java
  12. 11 7
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/OAuth2UserService.java
  13. 5 5
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/UserInfoRetriever.java
  14. 6 4
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java
  15. 3 3
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java

+ 16 - 17
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

@@ -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);

+ 4 - 4
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/AuthorizedClient.java → oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClient.java

@@ -22,8 +22,8 @@ import org.springframework.util.Assert;
 /**
  * A representation of an OAuth 2.0 <i>&quot;Authorized Client&quot;</i>.
  * <p>
- * A client is considered <i>&quot;authorized&quot;</i>
- * when it receives a successful response from the <i>Token Endpoint</i>.
+ * A client is considered <i>&quot;authorized&quot;</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");

+ 15 - 11
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java

@@ -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;
 	}
 }

+ 11 - 11
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java

@@ -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;

+ 6 - 6
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/OidcAuthorizedClient.java

@@ -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>&quot;Authorized Client&quot;</i>.
  * <p>
- * A client is considered <i>&quot;authorized&quot;</i>
- * when it receives a successful response from the <i>Token Endpoint</i>.
+ * A client is considered <i>&quot;authorized&quot;</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,

+ 11 - 12
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java

@@ -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;

+ 13 - 15
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java

@@ -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;

+ 5 - 4
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/CustomUserTypesOAuth2UserService.java

@@ -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) {

+ 6 - 4
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java

@@ -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(

+ 11 - 8
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DelegatingOAuth2UserService.java

@@ -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;
 	}
 }

+ 2 - 2
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/NimbusUserInfoRetriever.java

@@ -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());
 

+ 11 - 7
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/OAuth2UserService.java

@@ -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;
 
 }

+ 5 - 5
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/UserInfoRetriever.java

@@ -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;
 
 }

+ 6 - 4
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java

@@ -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(),

+ 3 - 3
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java

@@ -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);