mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-30 00:32:14 +00:00
Retrieving the UserInfo is conditional
Fixes gh-4451
This commit is contained in:
parent
f3828924ff
commit
ad91adf9dc
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.oidc.core;
|
||||
|
||||
import org.springframework.security.oauth2.core.AccessToken;
|
||||
|
||||
/**
|
||||
* The <i>scope</i> values defined by the <i>OpenID Connect Core 1.0</i> specification
|
||||
* that can be used to request {@link StandardClaim Claims}.
|
||||
* <p>
|
||||
* The scope(s) associated to an {@link AccessToken} determine what claims (resources)
|
||||
* will be available when they are used to access <i>OAuth 2.0 Protected Endpoints</i>,
|
||||
* such as the <i>UserInfo Endpoint</i>.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see StandardClaim
|
||||
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims">Requesting Claims using Scope Values</a>
|
||||
*/
|
||||
public interface OidcScope {
|
||||
|
||||
String OPENID = "openid";
|
||||
|
||||
String PROFILE = "profile";
|
||||
|
||||
String EMAIL = "email";
|
||||
|
||||
String ADDRESS = "address";
|
||||
|
||||
String PHONE = "phone";
|
||||
|
||||
}
|
@ -21,12 +21,14 @@ import org.springframework.security.oauth2.client.authentication.OAuth2UserAuthe
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -46,13 +48,18 @@ public class MainController {
|
||||
|
||||
@RequestMapping("/userinfo")
|
||||
public String userinfo(Model model, OAuth2UserAuthenticationToken authentication) {
|
||||
Map userAttributes = this.webClient
|
||||
.filter(oauth2Credentials(authentication))
|
||||
.get()
|
||||
.uri(authentication.getClientAuthentication().getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())
|
||||
.retrieve()
|
||||
.bodyToMono(Map.class)
|
||||
.block();
|
||||
Map userAttributes = Collections.emptyMap();
|
||||
String userInfoEndpointUri = authentication.getClientAuthentication().getClientRegistration()
|
||||
.getProviderDetails().getUserInfoEndpoint().getUri();
|
||||
if (!StringUtils.isEmpty(userInfoEndpointUri)) { // userInfoEndpointUri is optional for OIDC Clients
|
||||
userAttributes = this.webClient
|
||||
.filter(oauth2Credentials(authentication))
|
||||
.get()
|
||||
.uri(userInfoEndpointUri)
|
||||
.retrieve()
|
||||
.bodyToMono(Map.class)
|
||||
.block();
|
||||
}
|
||||
model.addAttribute("userAttributes", userAttributes);
|
||||
return "userinfo";
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user