diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index 2f33a462e5..1a4117baa7 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/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(); * } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AbstractOAuth2AuthorizationGrantAuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AbstractOAuth2AuthorizationGrantAuthenticationToken.java deleted file mode 100644 index 4638f70787..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AbstractOAuth2AuthorizationGrantAuthenticationToken.java +++ /dev/null @@ -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 authorization grant credential for a specific {@link AuthorizationGrantType}. - * - * @author Joe Grandja - * @since 5.0 - * @see AuthorizationGrantType - * @see Section 1.3 Authorization Grant - */ -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; - } -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java index f73354df5d..3885e19c65 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java +++ b/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 OAuth 2.0 {@link Authentication}. *

* This {@link Authentication} associates an {@link OAuth2User} Principal - * 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 UserInfo Endpoint. * * @author Joe Grandja * @since 5.0 * @see AbstractAuthenticationToken - * @see OAuth2AuthorizedClient * @see OAuth2User - * - * @param The type of OAuth 2.0 User - * @param The type of Authorized Client */ -public class OAuth2AuthenticationToken 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 authorities, C authorizedClient) { + public OAuth2AuthenticationToken(OAuth2User principal, + Collection 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 OAuth2AuthenticationTokenauthorization code grant credential for a specific client identified in {@link #getClientRegistration()}. - * - * @author Joe Grandja - * @since 5.0 - * @see AbstractOAuth2AuthorizationGrantAuthenticationToken - * @see ClientRegistration - * @see OAuth2AuthorizationRequest - * @see OAuth2AuthorizationResponse - * @see Section 1.3.1 Authorization Code Grant - */ -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; - } -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java index 65c20fd023..b285bfdb68 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java +++ b/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 OAuth 2.0 Authorization Code Grant Flow. + * An implementation of an {@link AuthenticationProvider} for OAuth 2.0 Login, + * which leverages the OAuth 2.0 Authorization Code Grant Flow. * * This {@link AuthenticationProvider} is responsible for authenticating - * an authorization code credential with the authorization server's Token Endpoint - * and if valid, exchanging it for an access token credential. + * an Authorization Code credential with the Authorization Server's Token Endpoint + * and if valid, exchanging it for an Access Token credential. *

* It will also obtain the user attributes of the End-User (Resource Owner) * from the UserInfo Endpoint 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 Section 4.1 Authorization Code Grant Flow * @see Section 4.1.3 Access Token Request @@ -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 mappedAuthorities = this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); - OAuth2AuthenticationToken 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); } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationToken.java new file mode 100644 index 0000000000..bdfcf43d57 --- /dev/null +++ b/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 OAuth 2.0 Login, + * which leverages the OAuth 2.0 Authorization Code Grant Flow. + * + * @author Joe Grandja + * @since 5.0 + * @see AbstractAuthenticationToken + * @see OAuth2User + * @see ClientRegistration + * @see OAuth2AuthorizationExchange + * @see OAuth2AccessToken + * @see Section 4.1 Authorization Code Grant Flow + */ +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 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; + } +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java index b5b57cffb4..c78286dac4 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java +++ b/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 OpenID Connect Core 1.0 Authorization Code Grant Flow. *

* This {@link AuthenticationProvider} is responsible for authenticating - * an authorization code credential with the authorization server's Token Endpoint - * and if valid, exchanging it for an access token credential. + * an Authorization Code credential with the Authorization Server's Token Endpoint + * and if valid, exchanging it for an Access Token credential. *

- * It will also obtain the user attributes of the End-User (resource owner) + * It will also obtain the user attributes of the End-User (Resource Owner) * from the UserInfo Endpoint using an {@link OAuth2UserService} * which will create a Principal 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 Section 3.1 Authorization Code Grant Flow * @see Section 3.1.3.1 Token Request @@ -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 mappedAuthorities = this.authoritiesMapper.mapAuthorities(oidcUser.getAuthorities()); - OAuth2AuthenticationToken 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) { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationToken.java new file mode 100644 index 0000000000..b61563b09e --- /dev/null +++ b/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 OpenID Connect 1.0 Authentication, + * which leverages the Authorization Code Flow. + * + * @author Joe Grandja + * @since 5.0 + * @see OAuth2LoginAuthenticationToken + * @see OidcIdToken + * @see 3.1 Authorization Code Flow + */ +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 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; + } +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java index af05907d49..7dac454c63 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java +++ b/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 Filter (the client). * *

  • - * This Filter will then create an {@link OAuth2AuthorizationCodeAuthenticationToken} with + * This Filter 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}). *
  • @@ -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 oauth2Authentication = - (OAuth2AuthenticationToken) 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; diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java index ba638ccf4d..b6ca644d6c 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java +++ b/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); diff --git a/samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java b/samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java index e87c1a5a97..c45f6c62ea 100644 --- a/samples/boot/oauth2login/src/integration-test/java/org/springframework/security/samples/OAuth2LoginApplicationTests.java +++ b/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 authorizedClientService() { + return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository); + } } } diff --git a/samples/boot/oauth2login/src/main/java/sample/config/OAuth2LoginConfig.java b/samples/boot/oauth2login/src/main/java/sample/config/OAuth2LoginConfig.java new file mode 100644 index 0000000000..fa9ab9f264 --- /dev/null +++ b/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 authorizedClientService() { + return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository); + } +} diff --git a/samples/boot/oauth2login/src/main/java/sample/web/MainController.java b/samples/boot/oauth2login/src/main/java/sample/web/MainController.java index 1b5a42585e..d9364274fe 100644 --- a/samples/boot/oauth2login/src/main/java/sample/web/MainController.java +++ b/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); });