1
0
mirror of synced 2026-08-05 01:36:56 +00:00

Retrieving the UserInfo is conditional

Fixes gh-4451
This commit is contained in:
Joe Grandja
2017-09-29 10:51:02 -04:00
parent f3828924ff
commit ad91adf9dc
5 changed files with 112 additions and 10 deletions
@@ -22,6 +22,9 @@ import org.springframework.security.core.authority.AuthorityUtils;
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.Set;
/**
* An implementation of an {@link AbstractAuthenticationToken}
@@ -70,4 +73,14 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
public AccessToken getAccessToken() {
return this.accessToken;
}
public Set<String> getAuthorizedScopes() {
// As per spec, in section 5.1 Successful Access Token Response
// https://tools.ietf.org/html/rfc6749#section-5.1
// If AccessToken.scopes is empty, then default to the scopes
// originally requested by the client in the Authorization Request
return (!CollectionUtils.isEmpty(this.getAccessToken().getScopes()) ?
this.getAccessToken().getScopes() :
this.getClientRegistration().getScope());
}
}
@@ -17,6 +17,7 @@ package org.springframework.security.oauth2.client.registration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.oidc.core.OidcScope;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -340,7 +341,10 @@ public class ClientRegistration {
Assert.notEmpty(this.scope, "scope cannot be empty");
Assert.hasText(this.authorizationUri, "authorizationUri cannot be empty");
Assert.hasText(this.tokenUri, "tokenUri cannot be empty");
Assert.hasText(this.userInfoUri, "userInfoUri cannot be empty");
if (!this.scope.contains(OidcScope.OPENID)) {
// userInfoUri is optional for OIDC Clients
Assert.hasText(this.userInfoUri, "userInfoUri cannot be empty");
}
Assert.hasText(this.clientName, "clientName cannot be empty");
Assert.hasText(this.registrationId, "registrationId cannot be empty");
}
@@ -21,13 +21,17 @@ import org.springframework.security.oauth2.client.authentication.OAuth2ClientAut
import org.springframework.security.oauth2.client.user.OAuth2UserService;
import org.springframework.security.oauth2.client.user.UserInfoRetriever;
import org.springframework.security.oauth2.client.user.nimbus.NimbusUserInfoRetriever;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.oidc.client.authentication.OidcClientAuthenticationToken;
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;
import org.springframework.security.oauth2.oidc.core.user.OidcUserAuthority;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -49,6 +53,8 @@ import java.util.Set;
*/
public class OidcUserService implements OAuth2UserService {
private UserInfoRetriever userInfoRetriever = new NimbusUserInfoRetriever();
private final Set<String> userInfoScopes = new HashSet<>(
Arrays.asList(OidcScope.PROFILE, OidcScope.EMAIL, OidcScope.ADDRESS, OidcScope.PHONE));
@Override
public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException {
@@ -57,8 +63,11 @@ public class OidcUserService implements OAuth2UserService {
}
OidcClientAuthenticationToken oidcClientAuthentication = (OidcClientAuthenticationToken)clientAuthentication;
Map<String, Object> userAttributes = this.getUserInfoRetriever().retrieve(oidcClientAuthentication);
UserInfo userInfo = new UserInfo(userAttributes);
UserInfo userInfo = null;
if (this.shouldRetrieveUserInfo(oidcClientAuthentication)) {
Map<String, Object> userAttributes = this.getUserInfoRetriever().retrieve(oidcClientAuthentication);
userInfo = new UserInfo(userAttributes);
}
GrantedAuthority authority = new OidcUserAuthority(oidcClientAuthentication.getIdToken(), userInfo);
Set<GrantedAuthority> authorities = new HashSet<>();
@@ -75,4 +84,28 @@ public class OidcUserService implements OAuth2UserService {
Assert.notNull(userInfoRetriever, "userInfoRetriever cannot be null");
this.userInfoRetriever = userInfoRetriever;
}
private boolean shouldRetrieveUserInfo(OidcClientAuthenticationToken oidcClientAuthentication) {
// Auto-disabled if UserInfo Endpoint URI is not provided
if (StringUtils.isEmpty(oidcClientAuthentication.getClientRegistration().getProviderDetails()
.getUserInfoEndpoint().getUri())) {
return false;
}
// The Claims requested by the profile, email, address, and phone scope values
// are returned from the UserInfo Endpoint (as described in Section 5.3.2),
// when a response_type value is used that results in an Access Token being issued.
// However, when no Access Token is issued, which is the case for the response_type=id_token,
// 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())) {
// 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 false;
}
}