From 5a584e5ccb08943bd9eb883ee879ecac233bb8ca Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Wed, 25 Oct 2017 12:20:31 -0400 Subject: [PATCH] Rename OAuth2/OIDC ClientAuthenticationToken -> AuthorizedClient Fixes gh-4695 --- ...cationToken.java => AuthorizedClient.java} | 42 +++++++------------ .../OAuth2LoginAuthenticationProvider.java | 21 ++++++---- .../CustomUserTypesOAuth2UserService.java | 10 ++--- .../userinfo/DefaultOAuth2UserService.java | 10 ++--- .../userinfo/DelegatingOAuth2UserService.java | 8 ++-- .../userinfo/NimbusUserInfoRetriever.java | 9 ++-- .../userinfo/OAuth2AuthenticationToken.java | 24 +++++------ .../userinfo/OAuth2UserService.java | 8 ++-- .../userinfo/UserInfoRetriever.java | 9 ++-- ...thorizationCodeAuthenticationProvider.java | 26 +++++++----- ...onToken.java => OidcAuthorizedClient.java} | 29 +++++++------ .../userinfo/OidcUserService.java | 28 ++++++------- .../OAuth2LoginAuthenticationFilterTests.java | 12 +++--- .../main/java/sample/web/MainController.java | 6 +-- 14 files changed, 117 insertions(+), 125 deletions(-) rename oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/{OAuth2ClientAuthenticationToken.java => AuthorizedClient.java} (61%) rename oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/{OidcClientAuthenticationToken.java => OidcAuthorizedClient.java} (60%) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2ClientAuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizedClient.java similarity index 61% rename from oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2ClientAuthenticationToken.java rename to oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizedClient.java index 88b26c7ef4..2c793584a9 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2ClientAuthenticationToken.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizedClient.java @@ -15,26 +15,22 @@ */ package org.springframework.security.oauth2.client.authentication; -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.SpringSecurityCoreVersion; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.AccessToken; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; -import java.util.Collections; import java.util.Set; /** - * An implementation of an {@link AbstractAuthenticationToken} - * that represents an OAuth 2.0 Client {@link Authentication}. - * + * A representation of an OAuth 2.0 "Authorized Client". *

- * A client is considered "authenticated", - * if it receives a successful response from the Token Endpoint. - * This {@link Authentication} associates the client identified in {@link #getClientRegistration()} - * to the {@link #getAccessToken()} granted by the resource owner. + * A client is considered "authorized" + * when it receives a successful response from the Token Endpoint. + *

+ * This class associates the {@link #getClientRegistration() Client} + * to the {@link #getAccessToken() Access Token} + * granted/authorized by the {@link #getPrincipalName() Resource Owner}. * * @author Joe Grandja * @since 5.0 @@ -42,34 +38,28 @@ import java.util.Set; * @see AccessToken * @see Section 5.1 Access Token Response */ -public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken { - private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; +public class AuthorizedClient { private final ClientRegistration clientRegistration; + private final String principalName; private final AccessToken accessToken; - public OAuth2ClientAuthenticationToken(ClientRegistration clientRegistration, AccessToken accessToken) { - super(Collections.emptyList()); + public AuthorizedClient(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"); this.clientRegistration = clientRegistration; + this.principalName = principalName; this.accessToken = accessToken; - this.setAuthenticated(true); // The Client is authenticated by the Authorization Server - } - - @Override - public Object getPrincipal() { - return this.getClientRegistration().getClientId(); - } - - @Override - public Object getCredentials() { - return ""; // No need to expose this.getClientRegistration().getClientSecret() } public ClientRegistration getClientRegistration() { return this.clientRegistration; } + public String getPrincipalName() { + return this.principalName; + } + public AccessToken getAccessToken() { return this.accessToken; } 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 bd5098c976..958d574c27 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 @@ -51,7 +51,7 @@ import java.util.Collection; * @see AuthorizationCodeAuthenticationToken * @see SecurityTokenRepository * @see OAuth2AuthenticationToken - * @see OAuth2ClientAuthenticationToken + * @see AuthorizedClient * @see OAuth2UserService * @see OAuth2User * @see Section 4.1 Authorization Code Grant Flow @@ -118,22 +118,25 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(), tokenResponse.getExpiresAt(), tokenResponse.getScopes()); - OAuth2ClientAuthenticationToken clientAuthentication = - new OAuth2ClientAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(), accessToken); - clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails()); + AuthorizedClient authorizedClient = new AuthorizedClient( + authorizationCodeAuthentication.getClientRegistration(), "unknown", accessToken); this.accessTokenRepository.saveSecurityToken( - clientAuthentication.getAccessToken(), - clientAuthentication.getClientRegistration()); + authorizedClient.getAccessToken(), + authorizedClient.getClientRegistration()); - OAuth2User oauth2User = this.userService.loadUser(clientAuthentication); + OAuth2User oauth2User = this.userService.loadUser(authorizedClient); + + // Update AuthorizedClient now that we know the 'principalName' + authorizedClient = new AuthorizedClient( + authorizationCodeAuthentication.getClientRegistration(), oauth2User.getName(), accessToken); Collection mappedAuthorities = this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken( - oauth2User, mappedAuthorities, clientAuthentication); - authenticationResult.setDetails(clientAuthentication.getDetails()); + oauth2User, mappedAuthorities, authorizedClient); + authenticationResult.setDetails(authorizationCodeAuthentication.getDetails()); return authenticationResult; } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/CustomUserTypesOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/CustomUserTypesOAuth2UserService.java index 4acf97bc1a..c23c041a4d 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/CustomUserTypesOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/CustomUserTypesOAuth2UserService.java @@ -15,10 +15,8 @@ */ package org.springframework.security.oauth2.client.authentication.userinfo; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.PropertyAccessorFactory; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.util.Assert; @@ -53,14 +51,14 @@ public class CustomUserTypesOAuth2UserService implements OAuth2UserService { } @Override - public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException { - URI userInfoUri = URI.create(clientAuthentication.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); + public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException { + URI userInfoUri = URI.create(authorizedClient.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); Class customUserType; if ((customUserType = this.customUserTypes.get(userInfoUri)) == null) { return null; } - return this.userInfoRetriever.retrieve(clientAuthentication, customUserType); + return this.userInfoRetriever.retrieve(authorizedClient, customUserType); } public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DefaultOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DefaultOAuth2UserService.java index 5b25eb7b43..3c074858cb 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DefaultOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DefaultOAuth2UserService.java @@ -17,7 +17,7 @@ package org.springframework.security.oauth2.client.authentication.userinfo; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -52,15 +52,15 @@ public class DefaultOAuth2UserService implements OAuth2UserService { private UserInfoRetriever userInfoRetriever = new NimbusUserInfoRetriever(); @Override - public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException { - String userNameAttributeName = clientAuthentication.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(); + public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException { + String userNameAttributeName = authorizedClient.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(); if (!StringUtils.hasText(userNameAttributeName)) { throw new IllegalArgumentException( "Missing required \"user name\" attribute name in UserInfoEndpoint for Client Registration: " + - clientAuthentication.getClientRegistration().getRegistrationId()); + authorizedClient.getClientRegistration().getRegistrationId()); } - Map userAttributes = this.userInfoRetriever.retrieve(clientAuthentication, Map.class); + Map userAttributes = this.userInfoRetriever.retrieve(authorizedClient, Map.class); GrantedAuthority authority = new OAuth2UserAuthority(userAttributes); Set authorities = new HashSet<>(); authorities.add(authority); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DelegatingOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DelegatingOAuth2UserService.java index eaa7bb3fe3..0e9c41c905 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DelegatingOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/DelegatingOAuth2UserService.java @@ -16,7 +16,7 @@ package org.springframework.security.oauth2.client.authentication.userinfo; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.util.Assert; @@ -30,7 +30,7 @@ import java.util.Objects; * to it's internal List of {@link OAuth2UserService}'s. *

* Each {@link OAuth2UserService} is given a chance to - * {@link OAuth2UserService#loadUser(OAuth2ClientAuthenticationToken) load} an {@link OAuth2User} + * {@link OAuth2UserService#loadUser(AuthorizedClient) load} an {@link OAuth2User} * with the first non-null {@link OAuth2User} being returned. * * @author Joe Grandja @@ -47,9 +47,9 @@ public class DelegatingOAuth2UserService implements OAuth2UserService { } @Override - public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException { + public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException { OAuth2User oauth2User = this.userServices.stream() - .map(userService -> userService.loadUser(clientAuthentication)) + .map(userService -> userService.loadUser(authorizedClient)) .filter(Objects::nonNull) .findFirst() .orElse(null); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/NimbusUserInfoRetriever.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/NimbusUserInfoRetriever.java index 0a1ec337cf..b4684a1c0a 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/NimbusUserInfoRetriever.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/NimbusUserInfoRetriever.java @@ -28,7 +28,7 @@ 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.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.util.Assert; @@ -37,7 +37,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.charset.Charset; -import java.util.Map; /** * An implementation of a {@link UserInfoRetriever} that uses the Nimbus OAuth 2.0 SDK internally. @@ -52,9 +51,9 @@ public class NimbusUserInfoRetriever implements UserInfoRetriever { private final HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); @Override - public T retrieve(OAuth2ClientAuthenticationToken clientAuthentication, Class returnType) throws OAuth2AuthenticationException { - URI userInfoUri = URI.create(clientAuthentication.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); - BearerAccessToken accessToken = new BearerAccessToken(clientAuthentication.getAccessToken().getTokenValue()); + public T retrieve(AuthorizedClient authorizedClient, Class returnType) throws OAuth2AuthenticationException { + URI userInfoUri = URI.create(authorizedClient.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); + BearerAccessToken accessToken = new BearerAccessToken(authorizedClient.getAccessToken().getTokenValue()); UserInfoRequest userInfoRequest = new UserInfoRequest(userInfoUri, accessToken); HTTPRequest httpRequest = userInfoRequest.toHTTPRequest(); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2AuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2AuthenticationToken.java index d25b56aa94..0549dba78f 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2AuthenticationToken.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/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.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.util.Assert; @@ -28,28 +28,28 @@ import java.util.Collection; /** * An implementation of an {@link AbstractAuthenticationToken} * that represents an OAuth 2.0 {@link Authentication}. - * *

- * This {@link Authentication} associates an {@link OAuth2User} principal to an - * {@link OAuth2ClientAuthenticationToken} which represents the "Authorized Client". + * This {@link Authentication} associates an {@link OAuth2User} principal + * to an {@link AuthorizedClient}. * * @author Joe Grandja * @since 5.0 * @see OAuth2User - * @see OAuth2ClientAuthenticationToken + * @see AuthorizedClient */ public class OAuth2AuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private final OAuth2User principal; - private final OAuth2ClientAuthenticationToken clientAuthentication; + private final AuthorizedClient authorizedClient; public OAuth2AuthenticationToken(OAuth2User principal, Collection authorities, - OAuth2ClientAuthenticationToken clientAuthentication) { + AuthorizedClient authorizedClient) { super(authorities); - Assert.notNull(clientAuthentication, "clientAuthentication cannot be null"); + Assert.notNull(principal, "principal cannot be null"); + Assert.notNull(authorizedClient, "authorizedClient cannot be null"); this.principal = principal; - this.clientAuthentication = clientAuthentication; - this.setAuthenticated(principal != null); + this.authorizedClient = authorizedClient; + this.setAuthenticated(true); } @Override @@ -63,7 +63,7 @@ public class OAuth2AuthenticationToken extends AbstractAuthenticationToken { return ""; } - public OAuth2ClientAuthenticationToken getClientAuthentication() { - return this.clientAuthentication; + public AuthorizedClient getAuthorizedClient() { + return this.authorizedClient; } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserService.java index e9e0ee4531..2a040eecb9 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserService.java @@ -16,24 +16,24 @@ package org.springframework.security.oauth2.client.authentication.userinfo; import org.springframework.security.core.AuthenticatedPrincipal; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; /** * Implementations of this interface are responsible for obtaining the user attributes * of the End-User (resource owner) from the UserInfo Endpoint - * using the provided {@link OAuth2ClientAuthenticationToken#getAccessToken()} + * using the provided {@link AuthorizedClient#getAccessToken()} * and returning an {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}. * * @author Joe Grandja * @since 5.0 - * @see OAuth2ClientAuthenticationToken + * @see AuthorizedClient * @see AuthenticatedPrincipal * @see OAuth2User */ public interface OAuth2UserService { - OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException; + OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException; } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/UserInfoRetriever.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/UserInfoRetriever.java index cdfecaf452..fdcfdbd09d 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/UserInfoRetriever.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/UserInfoRetriever.java @@ -15,23 +15,22 @@ */ package org.springframework.security.oauth2.client.authentication.userinfo; -import org.springframework.core.ParameterizedTypeReference; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; /** * A strategy for retrieving the user attributes * of the End-User (resource owner) from the UserInfo Endpoint - * using the provided {@link OAuth2ClientAuthenticationToken#getAccessToken()}. + * using the provided {@link AuthorizedClient#getAccessToken()}. * * @author Joe Grandja * @author Rob Winch * @since 5.0 - * @see OAuth2ClientAuthenticationToken + * @see AuthorizedClient * @see OAuth2UserService */ public interface UserInfoRetriever { - T retrieve(OAuth2ClientAuthenticationToken clientAuthentication, Class responseType) throws OAuth2AuthenticationException; + T retrieve(AuthorizedClient clientAuthentication, Class responseType) throws OAuth2AuthenticationException; } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java index 24bf0b7ea1..8894959f36 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java @@ -62,7 +62,7 @@ import java.util.Collection; * @since 5.0 * @see AuthorizationCodeAuthenticationToken * @see SecurityTokenRepository - * @see OidcClientAuthenticationToken + * @see OidcAuthorizedClient * @see OidcUserService * @see OidcUser * @see Section 3.1 Authorization Code Grant Flow @@ -142,28 +142,32 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati JwtDecoder jwtDecoder = this.jwtDecoderRegistry.getJwtDecoder(clientRegistration); if (jwtDecoder == null) { - throw new IllegalArgumentException("Failed to find a registered JwtDecoder for Client Registration: '" + clientRegistration.getRegistrationId() + - "'. Check to ensure you have configured the JwkSet URI."); + throw new IllegalArgumentException("Failed to find a registered JwtDecoder for Client Registration: '" + + clientRegistration.getRegistrationId() + "'. Check to ensure you have configured the JwkSet URI."); } Jwt jwt = jwtDecoder.decode((String)tokenResponse.getAdditionalParameters().get(OidcParameter.ID_TOKEN)); IdToken idToken = new IdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims()); - OidcClientAuthenticationToken clientAuthentication = - new OidcClientAuthenticationToken(clientRegistration, accessToken, idToken); - clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails()); + OidcAuthorizedClient authorizedClient = new OidcAuthorizedClient( + clientRegistration, idToken.getSubject(), accessToken, idToken); this.accessTokenRepository.saveSecurityToken( - clientAuthentication.getAccessToken(), - clientAuthentication.getClientRegistration()); + authorizedClient.getAccessToken(), + authorizedClient.getClientRegistration()); - OAuth2User oauth2User = this.userService.loadUser(clientAuthentication); + OAuth2User oauth2User = this.userService.loadUser(authorizedClient); + + // Update AuthorizedClient as the 'principalName' may have changed + // (the default IdToken.subject) from the result of userService.loadUser() + authorizedClient = new OidcAuthorizedClient( + clientRegistration, oauth2User.getName(), accessToken, idToken); Collection mappedAuthorities = this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken( - oauth2User, mappedAuthorities, clientAuthentication); - authenticationResult.setDetails(clientAuthentication.getDetails()); + oauth2User, mappedAuthorities, authorizedClient); + authenticationResult.setDetails(authorizationCodeAuthentication.getDetails()); return authenticationResult; } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcClientAuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizedClient.java similarity index 60% rename from oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcClientAuthenticationToken.java rename to oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizedClient.java index 173389b583..3f93a3197e 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcClientAuthenticationToken.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizedClient.java @@ -15,37 +15,36 @@ */ package org.springframework.security.oauth2.oidc.client.authentication; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.AccessToken; import org.springframework.security.oauth2.oidc.core.IdToken; import org.springframework.util.Assert; /** - * An {@link OAuth2ClientAuthenticationToken} that represents an - * OpenID Connect 1.0 Client {@link Authentication}. - * + * A representation of an OpenID Connect 1.0 "Authorized Client". *

- * A client is considered "authenticated", - * if it receives a successful response from the Token Endpoint. - * This {@link Authentication} associates the client identified in {@link #getClientRegistration()} - * to the {@link #getAccessToken()} granted by the resource owner along with the {@link #getIdToken()} - * containing Claims about the authentication of the End-User. + * A client is considered "authorized" + * when it receives a successful response from the Token Endpoint. + *

+ * This class associates the {@link #getClientRegistration() Client} + * to the {@link #getAccessToken() Access Token} + * granted/authorized by the {@link #getPrincipalName() Resource Owner}, along with + * the {@link #getIdToken() ID Token} which contains Claims about the authentication of the End-User. * * @author Joe Grandja * @since 5.0 * @see IdToken - * @see OAuth2ClientAuthenticationToken + * @see AuthorizedClient * @see 3.1.3.3 Successful Token Response */ -public class OidcClientAuthenticationToken extends OAuth2ClientAuthenticationToken { +public class OidcAuthorizedClient extends AuthorizedClient { private final IdToken idToken; - public OidcClientAuthenticationToken(ClientRegistration clientRegistration, - AccessToken accessToken, IdToken idToken) { + public OidcAuthorizedClient(ClientRegistration clientRegistration, String principalName, + AccessToken accessToken, IdToken idToken) { - super(clientRegistration, accessToken); + super(clientRegistration, principalName, accessToken); Assert.notNull(idToken, "idToken cannot be null"); this.idToken = idToken; } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/userinfo/OidcUserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/userinfo/OidcUserService.java index b054e92da7..1a5a45d278 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/userinfo/OidcUserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/userinfo/OidcUserService.java @@ -16,15 +16,15 @@ package org.springframework.security.oauth2.oidc.client.authentication.userinfo; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.client.authentication.userinfo.NimbusUserInfoRetriever; import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService; import org.springframework.security.oauth2.client.authentication.userinfo.UserInfoRetriever; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.oauth2.oidc.client.authentication.OidcClientAuthenticationToken; +import org.springframework.security.oauth2.oidc.client.authentication.OidcAuthorizedClient; import org.springframework.security.oauth2.oidc.core.OidcScope; import org.springframework.security.oauth2.oidc.core.UserInfo; import org.springframework.security.oauth2.oidc.core.user.DefaultOidcUser; @@ -47,7 +47,7 @@ import java.util.Set; * @author Joe Grandja * @since 5.0 * @see OAuth2UserService - * @see OidcClientAuthenticationToken + * @see OidcAuthorizedClient * @see DefaultOidcUser * @see UserInfo * @see UserInfoRetriever @@ -59,12 +59,12 @@ public class OidcUserService implements OAuth2UserService { Arrays.asList(OidcScope.PROFILE, OidcScope.EMAIL, OidcScope.ADDRESS, OidcScope.PHONE)); @Override - public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException { - OidcClientAuthenticationToken oidcClientAuthentication = (OidcClientAuthenticationToken)clientAuthentication; + public OAuth2User loadUser(AuthorizedClient authorizedClient) throws OAuth2AuthenticationException { + OidcAuthorizedClient oidcAuthorizedClient = (OidcAuthorizedClient)authorizedClient; UserInfo userInfo = null; - if (this.shouldRetrieveUserInfo(oidcClientAuthentication)) { - Map userAttributes = this.userInfoRetriever.retrieve(oidcClientAuthentication, Map.class); + if (this.shouldRetrieveUserInfo(oidcAuthorizedClient)) { + Map userAttributes = this.userInfoRetriever.retrieve(oidcAuthorizedClient, Map.class); userInfo = new UserInfo(userAttributes); // http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse @@ -74,17 +74,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(oidcClientAuthentication.getIdToken().getSubject())) { + if (!userInfo.getSubject().equals(oidcAuthorizedClient.getIdToken().getSubject())) { OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } } - GrantedAuthority authority = new OidcUserAuthority(oidcClientAuthentication.getIdToken(), userInfo); + GrantedAuthority authority = new OidcUserAuthority(oidcAuthorizedClient.getIdToken(), userInfo); Set authorities = new HashSet<>(); authorities.add(authority); - return new DefaultOidcUser(authorities, oidcClientAuthentication.getIdToken(), userInfo); + return new DefaultOidcUser(authorities, oidcAuthorizedClient.getIdToken(), userInfo); } public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) { @@ -92,9 +92,9 @@ public class OidcUserService implements OAuth2UserService { this.userInfoRetriever = userInfoRetriever; } - private boolean shouldRetrieveUserInfo(OidcClientAuthenticationToken oidcClientAuthentication) { + private boolean shouldRetrieveUserInfo(OidcAuthorizedClient oidcAuthorizedClient) { // Auto-disabled if UserInfo Endpoint URI is not provided - if (StringUtils.isEmpty(oidcClientAuthentication.getClientRegistration().getProviderDetails() + if (StringUtils.isEmpty(oidcAuthorizedClient.getClientRegistration().getProviderDetails() .getUserInfoEndpoint().getUri())) { return false; @@ -107,10 +107,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( - oidcClientAuthentication.getClientRegistration().getAuthorizationGrantType())) { + oidcAuthorizedClient.getClientRegistration().getAuthorizationGrantType())) { // Return true if there is at least one match between the authorized scope(s) and UserInfo scope(s) - return oidcClientAuthentication.getAuthorizedScopes().stream().anyMatch(userInfoScopes::contains); + return oidcAuthorizedClient.getAuthorizedScopes().stream().anyMatch(userInfoScopes::contains); } return false; 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 3ce9d8c54a..4872ba9ed1 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 @@ -28,7 +28,7 @@ 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.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.AuthorizedClient; import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; @@ -98,13 +98,13 @@ public class OAuth2LoginAuthenticationFilterTests { @Test public void doFilterWhenAuthorizationCodeSuccessResponseThenAuthenticationSuccessHandlerIsCalled() throws Exception { ClientRegistration clientRegistration = TestUtil.githubClientRegistration(); - OAuth2ClientAuthenticationToken clientAuthentication = new OAuth2ClientAuthenticationToken( - clientRegistration, mock(AccessToken.class)); + AuthorizedClient authorizedClient = new AuthorizedClient( + clientRegistration, "principal", mock(AccessToken.class)); OAuth2AuthenticationToken userAuthentication = new OAuth2AuthenticationToken( - mock(OAuth2User.class), AuthorityUtils.createAuthorityList("ROLE_USER"), clientAuthentication); + mock(OAuth2User.class), AuthorityUtils.createAuthorityList("ROLE_USER"), authorizedClient); SecurityContextHolder.getContext().setAuthentication(userAuthentication); AuthenticationManager authenticationManager = mock(AuthenticationManager.class); - Mockito.when(authenticationManager.authenticate(Matchers.any(Authentication.class))).thenReturn(clientAuthentication); + Mockito.when(authenticationManager.authenticate(Matchers.any(Authentication.class))).thenReturn(userAuthentication); OAuth2LoginAuthenticationFilter filter = Mockito.spy(setupFilter(authenticationManager, clientRegistration)); AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class); @@ -128,7 +128,7 @@ public class OAuth2LoginAuthenticationFilterTests { ArgumentCaptor authenticationArgCaptor = ArgumentCaptor.forClass(Authentication.class); Mockito.verify(successHandler).onAuthenticationSuccess(Matchers.any(HttpServletRequest.class), Matchers.any(HttpServletResponse.class), authenticationArgCaptor.capture()); - Assertions.assertThat(authenticationArgCaptor.getValue()).isEqualTo(clientAuthentication); + Assertions.assertThat(authenticationArgCaptor.getValue()).isEqualTo(userAuthentication); } @Test 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 94871333ac..d9fc16c137 100644 --- a/samples/boot/oauth2login/src/main/java/sample/web/MainController.java +++ b/samples/boot/oauth2login/src/main/java/sample/web/MainController.java @@ -40,14 +40,14 @@ public class MainController { @RequestMapping("/") public String index(Model model, @AuthenticationPrincipal OAuth2User user, OAuth2AuthenticationToken authentication) { model.addAttribute("userName", user.getName()); - model.addAttribute("clientName", authentication.getClientAuthentication().getClientRegistration().getClientName()); + model.addAttribute("clientName", authentication.getAuthorizedClient().getClientRegistration().getClientName()); return "index"; } @RequestMapping("/userinfo") public String userinfo(Model model, OAuth2AuthenticationToken authentication) { Map userAttributes = Collections.emptyMap(); - String userInfoEndpointUri = authentication.getClientAuthentication().getClientRegistration() + String userInfoEndpointUri = authentication.getAuthorizedClient().getClientRegistration() .getProviderDetails().getUserInfoEndpoint().getUri(); if (!StringUtils.isEmpty(userInfoEndpointUri)) { // userInfoEndpointUri is optional for OIDC Clients userAttributes = WebClient.builder() @@ -67,7 +67,7 @@ public class MainController { return ExchangeFilterFunction.ofRequestProcessor( clientRequest -> { ClientRequest authorizedRequest = ClientRequest.from(clientRequest) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + authentication.getClientAuthentication().getAccessToken().getTokenValue()) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + authentication.getAuthorizedClient().getAccessToken().getTokenValue()) .build(); return Mono.just(authorizedRequest); });