浏览代码

Re-factor OAuth2AuthorizationCodeAuthenticationToken

Fixes gh-4730
Joe Grandja 7 年之前
父节点
当前提交
0c68eb1821
共有 13 个文件被更改,包括 344 次插入190 次删除
  1. 1 1
      config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
  2. 0 47
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AbstractOAuth2AuthorizationGrantAuthenticationToken.java
  3. 13 15
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java
  4. 0 69
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationToken.java
  5. 15 16
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java
  6. 116 0
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationToken.java
  7. 16 17
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java
  8. 81 0
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationToken.java
  9. 20 11
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java
  10. 11 5
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java
  11. 12 0
      samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java
  12. 39 0
      samples/boot/oauth2login/src/main/java/sample/config/OAuth2LoginConfig.java
  13. 20 9
      samples/boot/oauth2login/src/main/java/sample/web/MainController.java

+ 1 - 1
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -1014,7 +1014,7 @@ public final class HttpSecurity extends
 	 * 	}
 	 *
 	 * 	@Bean
-	 * 	public AuthorizationGrantTokenExchanger<OAuth2AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger() {
+	 * 	public AuthorizationGrantTokenExchanger<OAuth2LoginAuthenticationToken> authorizationCodeTokenExchanger() {
 	 * 		// Custom implementation that exchanges an "Authorization Code Grant" for an "Access Token"
 	 * 		return new AuthorizationCodeTokenExchangerImpl();
 	 * 	}

+ 0 - 47
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AbstractOAuth2AuthorizationGrantAuthenticationToken.java

@@ -1,47 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.security.oauth2.client.authentication;
-
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-import org.springframework.security.core.SpringSecurityCoreVersion;
-import org.springframework.security.oauth2.core.AuthorizationGrantType;
-import org.springframework.util.Assert;
-
-import java.util.Collections;
-
-/**
- * Base implementation of an {@link AbstractAuthenticationToken} that holds
- * an <i>authorization grant</i> credential for a specific {@link AuthorizationGrantType}.
- *
- * @author Joe Grandja
- * @since 5.0
- * @see AuthorizationGrantType
- * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.3">Section 1.3 Authorization Grant</a>
- */
-public abstract class AbstractOAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthenticationToken {
-	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
-	private final AuthorizationGrantType authorizationGrantType;
-
-	protected AbstractOAuth2AuthorizationGrantAuthenticationToken(AuthorizationGrantType authorizationGrantType) {
-		super(Collections.emptyList());
-		Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null");
-		this.authorizationGrantType = authorizationGrantType;
-	}
-
-	public AuthorizationGrantType getGrantType() {
-		return this.authorizationGrantType;
-	}
-}

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

@@ -19,7 +19,6 @@ 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.OAuth2AuthorizedClient;
 import org.springframework.security.oauth2.core.user.OAuth2User;
 import org.springframework.util.Assert;
 
@@ -30,34 +29,33 @@ import java.util.Collection;
  * that represents an <i>OAuth 2.0</i> {@link Authentication}.
  * <p>
  * This {@link Authentication} associates an {@link OAuth2User} <code>Principal</code>
- * to an {@link OAuth2AuthorizedClient}, which the End-User (Principal) granted authorization to
+ * to the identifier of the {@link #getAuthorizedClientRegistrationId() Authorized Client},
+ * 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
- *
- * @param <U> The type of <i>OAuth 2.0 User</i>
- * @param <C> The type of <i>Authorized Client</i>
  */
-public class OAuth2AuthenticationToken<U extends OAuth2User, C extends OAuth2AuthorizedClient> extends AbstractAuthenticationToken {
+public class OAuth2AuthenticationToken extends AbstractAuthenticationToken {
 	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
-	private final U principal;
-	private final C authorizedClient;
+	private final OAuth2User principal;
+	private final String authorizedClientRegistrationId;
 
-	public OAuth2AuthenticationToken(U principal, Collection<? extends GrantedAuthority> authorities, C authorizedClient) {
+	public OAuth2AuthenticationToken(OAuth2User principal,
+									Collection<? extends GrantedAuthority> authorities,
+									String authorizedClientRegistrationId) {
 		super(authorities);
 		Assert.notNull(principal, "principal cannot be null");
-		Assert.notNull(authorizedClient, "authorizedClient cannot be null");
+		Assert.hasText(authorizedClientRegistrationId, "authorizedClientRegistrationId cannot be empty");
 		this.principal = principal;
-		this.authorizedClient = authorizedClient;
+		this.authorizedClientRegistrationId = authorizedClientRegistrationId;
 		this.setAuthenticated(true);
 	}
 
 	@Override
-	public U getPrincipal() {
+	public OAuth2User getPrincipal() {
 		return this.principal;
 	}
 
@@ -67,7 +65,7 @@ public class OAuth2AuthenticationToken<U extends OAuth2User, C extends OAuth2Aut
 		return "";
 	}
 
-	public C getAuthorizedClient() {
-		return this.authorizedClient;
+	public String getAuthorizedClientRegistrationId() {
+		return this.authorizedClientRegistrationId;
 	}
 }

+ 0 - 69
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationToken.java

@@ -1,69 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.security.oauth2.client.authentication;
-
-import org.springframework.security.oauth2.client.registration.ClientRegistration;
-import org.springframework.security.oauth2.core.AuthorizationGrantType;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
-import org.springframework.util.Assert;
-
-/**
- * An implementation of an {@link AbstractOAuth2AuthorizationGrantAuthenticationToken} that holds
- * an <i>authorization code grant</i> credential for a specific client identified in {@link #getClientRegistration()}.
- *
- * @author Joe Grandja
- * @since 5.0
- * @see AbstractOAuth2AuthorizationGrantAuthenticationToken
- * @see ClientRegistration
- * @see OAuth2AuthorizationRequest
- * @see OAuth2AuthorizationResponse
- * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.3.1">Section 1.3.1 Authorization Code Grant</a>
- */
-public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractOAuth2AuthorizationGrantAuthenticationToken {
-	private final ClientRegistration clientRegistration;
-	private final OAuth2AuthorizationExchange authorizationExchange;
-
-	public OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
-														OAuth2AuthorizationExchange authorizationExchange) {
-
-		super(AuthorizationGrantType.AUTHORIZATION_CODE);
-		Assert.notNull(clientRegistration, "clientRegistration cannot be null");
-		Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
-		this.clientRegistration = clientRegistration;
-		this.authorizationExchange = authorizationExchange;
-		this.setAuthenticated(false);
-	}
-
-	@Override
-	public Object getPrincipal() {
-		return "";
-	}
-
-	@Override
-	public Object getCredentials() {
-		return "";
-	}
-
-	public ClientRegistration getClientRegistration() {
-		return this.clientRegistration;
-	}
-
-	public OAuth2AuthorizationExchange getAuthorizationExchange() {
-		return this.authorizationExchange;
-	}
-}

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

@@ -20,7 +20,6 @@ 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.OAuth2AuthorizedClient;
 import org.springframework.security.oauth2.client.endpoint.AuthorizationGrantTokenExchanger;
 import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
 import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
@@ -37,12 +36,12 @@ import org.springframework.util.Assert;
 import java.util.Collection;
 
 /**
- * An implementation of an {@link AuthenticationProvider}
- * for the <i>OAuth 2.0 Authorization Code Grant Flow</i>.
+ * An implementation of an {@link AuthenticationProvider} for <i>OAuth 2.0 Login</i>,
+ * which leverages the <i>OAuth 2.0 Authorization Code Grant</i> Flow.
  *
  * This {@link AuthenticationProvider} is responsible for authenticating
- * 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.
+ * 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)
  * from the <i>UserInfo Endpoint</i> using an {@link OAuth2UserService}
@@ -50,10 +49,9 @@ import java.util.Collection;
  *
  * @author Joe Grandja
  * @since 5.0
- * @see OAuth2AuthorizationCodeAuthenticationToken
- * @see OAuth2AuthenticationToken
+ * @see OAuth2LoginAuthenticationToken
+ * @see AuthorizationGrantTokenExchanger
  * @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>
@@ -78,8 +76,8 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
 
 	@Override
 	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
-		OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
-				(OAuth2AuthorizationCodeAuthenticationToken) authentication;
+		OAuth2LoginAuthenticationToken authorizationCodeAuthentication =
+			(OAuth2LoginAuthenticationToken) authentication;
 
 		// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
 		// scope
@@ -124,14 +122,15 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
 		OAuth2User oauth2User = this.userService.loadUser(
 			new OAuth2UserRequest(authorizationCodeAuthentication.getClientRegistration(), accessToken));
 
-		OAuth2AuthorizedClient oauth2AuthorizedClient = new OAuth2AuthorizedClient(
-			authorizationCodeAuthentication.getClientRegistration(), oauth2User.getName(), accessToken);
-
 		Collection<? extends GrantedAuthority> mappedAuthorities =
 			this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
 
-		OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> authenticationResult =
-			new OAuth2AuthenticationToken<>(oauth2User, mappedAuthorities, oauth2AuthorizedClient);
+		OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
+			oauth2User,
+			mappedAuthorities,
+			authorizationCodeAuthentication.getClientRegistration(),
+			authorizationCodeAuthentication.getAuthorizationExchange(),
+			accessToken);
 		authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
 
 		return authenticationResult;
@@ -144,6 +143,6 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
 
 	@Override
 	public boolean supports(Class<?> authentication) {
-		return OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication);
+		return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
 	}
 }

+ 116 - 0
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationToken.java

@@ -0,0 +1,116 @@
+/*
+ * Copyright 2012-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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.client.authentication;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.SpringSecurityCoreVersion;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
+import org.springframework.security.oauth2.core.user.OAuth2User;
+import org.springframework.util.Assert;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * An {@link AbstractAuthenticationToken} for <i>OAuth 2.0 Login</i>,
+ * which leverages the <i>OAuth 2.0 Authorization Code Grant</i> Flow.
+ *
+ * @author Joe Grandja
+ * @since 5.0
+ * @see AbstractAuthenticationToken
+ * @see OAuth2User
+ * @see ClientRegistration
+ * @see OAuth2AuthorizationExchange
+ * @see OAuth2AccessToken
+ * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a>
+ */
+public class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken {
+	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
+	private OAuth2User principal;
+	private ClientRegistration clientRegistration;
+	private OAuth2AuthorizationExchange authorizationExchange;
+	private OAuth2AccessToken accessToken;
+
+	/**
+	 * This constructor should be used when the Authorization Request/Response is complete.
+	 *
+	 * @param clientRegistration
+	 * @param authorizationExchange
+	 */
+	public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,
+											OAuth2AuthorizationExchange authorizationExchange) {
+
+		super(Collections.emptyList());
+		Assert.notNull(clientRegistration, "clientRegistration cannot be null");
+		Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
+		this.clientRegistration = clientRegistration;
+		this.authorizationExchange = authorizationExchange;
+		this.setAuthenticated(false);
+	}
+
+	/**
+	 * This constructor should be used when the Access Token Request/Response is complete,
+	 * which indicates that the Authorization Code Grant flow has fully completed
+	 * and OAuth 2.0 Login has been achieved.
+	 *
+	 * @param principal
+	 * @param authorities
+	 * @param clientRegistration
+	 * @param authorizationExchange
+	 * @param accessToken
+	 */
+	public OAuth2LoginAuthenticationToken(OAuth2User principal,
+											Collection<? extends GrantedAuthority> authorities,
+											ClientRegistration clientRegistration,
+											OAuth2AuthorizationExchange authorizationExchange,
+											OAuth2AccessToken accessToken) {
+		super(authorities);
+		Assert.notNull(principal, "principal cannot be null");
+		Assert.notNull(clientRegistration, "clientRegistration cannot be null");
+		Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
+		Assert.notNull(accessToken, "accessToken cannot be null");
+		this.principal = principal;
+		this.clientRegistration = clientRegistration;
+		this.authorizationExchange = authorizationExchange;
+		this.accessToken = accessToken;
+		this.setAuthenticated(true);
+	}
+
+	@Override
+	public OAuth2User getPrincipal() {
+		return this.principal;
+	}
+
+	@Override
+	public Object getCredentials() {
+		return "";
+	}
+
+	public ClientRegistration getClientRegistration() {
+		return this.clientRegistration;
+	}
+
+	public OAuth2AuthorizationExchange getAuthorizationExchange() {
+		return this.authorizationExchange;
+	}
+
+	public OAuth2AccessToken getAccessToken() {
+		return this.accessToken;
+	}
+}

+ 16 - 17
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java

@@ -20,12 +20,10 @@ 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.authentication.OAuth2LoginAuthenticationToken;
 import org.springframework.security.oauth2.client.endpoint.AuthorizationGrantTokenExchanger;
-import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
-import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
 import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
 import org.springframework.security.oauth2.client.jwt.JwtDecoderRegistry;
-import org.springframework.security.oauth2.client.oidc.OidcAuthorizedClient;
 import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
 import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -55,19 +53,18 @@ import java.util.List;
  * for the <i>OpenID Connect Core 1.0 Authorization Code Grant Flow</i>.
  * <p>
  * This {@link AuthenticationProvider} is responsible for authenticating
- * 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.
+ * 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 OidcUser}.
  *
  * @author Joe Grandja
  * @since 5.0
- * @see OAuth2AuthorizationCodeAuthenticationToken
- * @see OAuth2AuthenticationToken
+ * @see OidcAuthorizationCodeAuthenticationToken
+ * @see AuthorizationGrantTokenExchanger
  * @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>
@@ -97,8 +94,8 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
 
 	@Override
 	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
-		OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
-				(OAuth2AuthorizationCodeAuthenticationToken) authentication;
+		OAuth2LoginAuthenticationToken authorizationCodeAuthentication =
+			(OAuth2LoginAuthenticationToken) authentication;
 
 		// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
 		// scope
@@ -160,14 +157,16 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
 		OidcUser oidcUser = this.userService.loadUser(
 			new OidcUserRequest(clientRegistration, accessToken, idToken));
 
-		OidcAuthorizedClient oidcAuthorizedClient = new OidcAuthorizedClient(
-			clientRegistration, oidcUser.getName(), accessToken, idToken);
-
 		Collection<? extends GrantedAuthority> mappedAuthorities =
 			this.authoritiesMapper.mapAuthorities(oidcUser.getAuthorities());
 
-		OAuth2AuthenticationToken<OidcUser, OidcAuthorizedClient> authenticationResult =
-			new OAuth2AuthenticationToken<>(oidcUser, mappedAuthorities, oidcAuthorizedClient);
+		OidcAuthorizationCodeAuthenticationToken authenticationResult = new OidcAuthorizationCodeAuthenticationToken(
+			oidcUser,
+			mappedAuthorities,
+			authorizationCodeAuthentication.getClientRegistration(),
+			authorizationCodeAuthentication.getAuthorizationExchange(),
+			accessToken,
+			idToken);
 		authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
 
 		return authenticationResult;
@@ -180,7 +179,7 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
 
 	@Override
 	public boolean supports(Class<?> authentication) {
-		return OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication);
+		return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
 	}
 
 	private void validateIdToken(OidcIdToken idToken, ClientRegistration clientRegistration) {

+ 81 - 0
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationToken.java

@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012-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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.client.oidc.authentication;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.util.Assert;
+
+import java.util.Collection;
+
+/**
+ * An {@link OAuth2LoginAuthenticationToken} for <i>OpenID Connect 1.0 Authentication</i>,
+ * which leverages the <i>Authorization Code Flow</i>.
+ *
+ * @author Joe Grandja
+ * @since 5.0
+ * @see OAuth2LoginAuthenticationToken
+ * @see OidcIdToken
+ * @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">3.1 Authorization Code Flow</a>
+ */
+public class OidcAuthorizationCodeAuthenticationToken extends OAuth2LoginAuthenticationToken {
+	private OidcIdToken idToken;
+
+	/**
+	 * This constructor should be used when the Authentication Request/Response is complete.
+	 *
+	 * @param clientRegistration
+	 * @param authorizationExchange
+	 */
+	public OidcAuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
+													OAuth2AuthorizationExchange authorizationExchange) {
+
+		super(clientRegistration, authorizationExchange);
+	}
+
+	/**
+	 * This constructor should be used when the Token Request/Response is complete,
+	 * which indicates that the Authorization Code Flow has fully completed
+	 * and OpenID Connect 1.0 Authentication has been achieved.
+	 *
+	 * @param principal
+	 * @param authorities
+	 * @param clientRegistration
+	 * @param authorizationExchange
+	 * @param accessToken
+	 * @param idToken
+	 */
+	public OidcAuthorizationCodeAuthenticationToken(OidcUser principal,
+													Collection<? extends GrantedAuthority> authorities,
+													ClientRegistration clientRegistration,
+													OAuth2AuthorizationExchange authorizationExchange,
+													OAuth2AccessToken accessToken,
+													OidcIdToken idToken) {
+
+		super(principal, authorities, clientRegistration, authorizationExchange, accessToken);
+		Assert.notNull(idToken, "idToken cannot be null");
+		this.idToken = idToken;
+	}
+
+	public OidcIdToken getIdToken() {
+		return this.idToken;
+	}
+}

+ 20 - 11
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java

@@ -21,8 +21,8 @@ import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
-import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
 import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
+import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
 import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository;
@@ -35,7 +35,6 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExch
 import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
 import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
-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;
@@ -60,7 +59,7 @@ import java.io.IOException;
  *	and redirect the end-user's user-agent back to this <code>Filter</code> (the client).
  * </li>
  * <li>
- *  This <code>Filter</code> will then create an {@link OAuth2AuthorizationCodeAuthenticationToken} with
+ *  This <code>Filter</code> will then create an {@link OAuth2LoginAuthenticationToken} with
  *  the {@link OAuth2ParameterNames#CODE} received in the previous step and delegate it to
  *  {@link OAuth2LoginAuthenticationProvider#authenticate(Authentication)} (indirectly via {@link AuthenticationManager}).
  * </li>
@@ -69,7 +68,7 @@ import java.io.IOException;
  * @author Joe Grandja
  * @since 5.0
  * @see AbstractAuthenticationProcessingFilter
- * @see OAuth2AuthorizationCodeAuthenticationToken
+ * @see OAuth2LoginAuthenticationToken
  * @see OAuth2AuthenticationToken
  * @see OAuth2LoginAuthenticationProvider
  * @see OAuth2AuthorizationRequest
@@ -136,19 +135,29 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
 			.redirectUri(authorizationRequest.getRedirectUri())
 			.build();
 
-		OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = new OAuth2AuthorizationCodeAuthenticationToken(
+		OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(
 				clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
-		authorizationCodeAuthentication.setDetails(this.authenticationDetailsSource.buildDetails(request));
+		authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
 
-		OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> oauth2Authentication =
-			(OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient>) this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
+		OAuth2LoginAuthenticationToken authenticationResult =
+			(OAuth2LoginAuthenticationToken)this.getAuthenticationManager().authenticate(authenticationRequest);
+
+		OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
+			authenticationResult.getPrincipal(),
+			authenticationResult.getAuthorities(),
+			authenticationResult.getClientRegistration().getRegistrationId());
+
+		OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
+			authenticationResult.getClientRegistration(),
+			oauth2Authentication.getName(),
+			authenticationResult.getAccessToken());
 
 		this.authorizedClientService.saveAuthorizedClient(
-			oauth2Authentication.getAuthorizedClient(), oauth2Authentication);
+			authorizedClient, oauth2Authentication);
 
 		this.accessTokenRepository.saveToken(
-			oauth2Authentication.getAuthorizedClient().getAccessToken(),
-			oauth2Authentication.getAuthorizedClient().getClientRegistration(),
+			authorizedClient.getAccessToken(),
+			authorizedClient.getClientRegistration(),
 			oauth2Authentication);
 
 		return oauth2Authentication;

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

@@ -27,9 +27,9 @@ 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.client.OAuth2AuthorizedClient;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
 import org.springframework.security.oauth2.core.OAuth2AccessToken;
@@ -48,6 +48,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 /**
  * Tests {@link OAuth2LoginAuthenticationFilter}.
@@ -99,13 +100,18 @@ public class OAuth2LoginAuthenticationFilterTests {
 	@Test
 	public void doFilterWhenAuthorizationCodeSuccessResponseThenAuthenticationSuccessHandlerIsCalled() throws Exception {
 		ClientRegistration clientRegistration = TestUtil.githubClientRegistration();
-		OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
-			clientRegistration, "principal", mock(OAuth2AccessToken.class));
+		OAuth2User oauth2User = mock(OAuth2User.class);
+		when(oauth2User.getName()).thenReturn("principal name");
+		OAuth2LoginAuthenticationToken loginAuthentication = mock(OAuth2LoginAuthenticationToken.class);
+		when(loginAuthentication.getPrincipal()).thenReturn(oauth2User);
+		when(loginAuthentication.getClientRegistration()).thenReturn(clientRegistration);
+		when(loginAuthentication.getAccessToken()).thenReturn(mock(OAuth2AccessToken.class));
+
 		OAuth2AuthenticationToken userAuthentication = new OAuth2AuthenticationToken(
-			mock(OAuth2User.class), AuthorityUtils.createAuthorityList("ROLE_USER"), authorizedClient);
+			oauth2User, AuthorityUtils.NO_AUTHORITIES, clientRegistration.getRegistrationId());
 		SecurityContextHolder.getContext().setAuthentication(userAuthentication);
 		AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
-		Mockito.when(authenticationManager.authenticate(Matchers.any(Authentication.class))).thenReturn(userAuthentication);
+		when(authenticationManager.authenticate(Matchers.any(Authentication.class))).thenReturn(loginAuthentication);
 
 		OAuth2LoginAuthenticationFilter filter = Mockito.spy(setupFilter(authenticationManager, clientRegistration));
 		AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);

+ 12 - 0
samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java

@@ -30,12 +30,16 @@ import org.springframework.boot.SpringBootConfiguration;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
 import org.springframework.security.oauth2.client.endpoint.AuthorizationGrantTokenExchanger;
 import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -388,5 +392,13 @@ public class OAuth2LoginApplicationTests {
 	@EnableAutoConfiguration
 	@ComponentScan(basePackages = "sample.web")
 	public static class SpringBootApplicationTestConfig {
+
+		@Autowired
+		private ClientRegistrationRepository clientRegistrationRepository;
+
+		@Bean
+		public OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService() {
+			return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository);
+		}
 	}
 }

+ 39 - 0
samples/boot/oauth2login/src/main/java/sample/config/OAuth2LoginConfig.java

@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+
+/**
+ * @author Joe Grandja
+ */
+@Configuration
+public class OAuth2LoginConfig {
+
+	@Autowired
+	private ClientRegistrationRepository clientRegistrationRepository;
+
+	@Bean
+	public OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService() {
+		return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository);
+	}
+}

+ 20 - 9
samples/boot/oauth2login/src/main/java/sample/web/MainController.java

@@ -15,10 +15,11 @@
  */
 package sample.web;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpHeaders;
-import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
-import org.springframework.security.oauth2.core.user.OAuth2User;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.util.StringUtils;
@@ -37,21 +38,26 @@ import java.util.Map;
 @Controller
 public class MainController {
 
+	@Autowired
+	private OAuth2AuthorizedClientService authorizedClientService;
+
 	@RequestMapping("/")
-	public String index(Model model, @AuthenticationPrincipal OAuth2User user, OAuth2AuthenticationToken authentication) {
-		model.addAttribute("userName", user.getName());
-		model.addAttribute("clientName", authentication.getAuthorizedClient().getClientRegistration().getClientName());
+	public String index(Model model, OAuth2AuthenticationToken authentication) {
+		OAuth2AuthorizedClient authorizedClient = this.getAuthorizedClient(authentication);
+		model.addAttribute("userName", authentication.getName());
+		model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName());
 		return "index";
 	}
 
 	@RequestMapping("/userinfo")
 	public String userinfo(Model model, OAuth2AuthenticationToken authentication) {
+		OAuth2AuthorizedClient authorizedClient = this.getAuthorizedClient(authentication);
 		Map userAttributes = Collections.emptyMap();
-		String userInfoEndpointUri = authentication.getAuthorizedClient().getClientRegistration()
+		String userInfoEndpointUri = authorizedClient.getClientRegistration()
 			.getProviderDetails().getUserInfoEndpoint().getUri();
 		if (!StringUtils.isEmpty(userInfoEndpointUri)) {	// userInfoEndpointUri is optional for OIDC Clients
 			userAttributes = WebClient.builder()
-				.filter(oauth2Credentials(authentication))
+				.filter(oauth2Credentials(authorizedClient))
 				.build()
 				.get()
 				.uri(userInfoEndpointUri)
@@ -63,11 +69,16 @@ public class MainController {
 		return "userinfo";
 	}
 
-	private ExchangeFilterFunction oauth2Credentials(OAuth2AuthenticationToken authentication) {
+	private OAuth2AuthorizedClient getAuthorizedClient(OAuth2AuthenticationToken authentication) {
+		return this.authorizedClientService.loadAuthorizedClient(
+			authentication.getAuthorizedClientRegistrationId(), authentication);
+	}
+
+	private ExchangeFilterFunction oauth2Credentials(OAuth2AuthorizedClient authorizedClient) {
 		return ExchangeFilterFunction.ofRequestProcessor(
 			clientRequest -> {
 				ClientRequest authorizedRequest = ClientRequest.from(clientRequest)
-					.header(HttpHeaders.AUTHORIZATION, "Bearer " + authentication.getAuthorizedClient().getAccessToken().getTokenValue())
+					.header(HttpHeaders.AUTHORIZATION, "Bearer " + authorizedClient.getAccessToken().getTokenValue())
 					.build();
 				return Mono.just(authorizedRequest);
 			});