diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java index 31f181213ae..7025ecb5c98 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java @@ -173,8 +173,15 @@ private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) { .equals(userRequest.getClientRegistration().getAuthorizationGrantType())) { // Return true if there is at least one match between the authorized scope(s) // and accessible scope(s) + // As per spec, in Section 5.1 Successful Access Token Response + // https://www.rfc-editor.org/rfc/rfc6749#section-5.1 + // If AccessTokenResponse.scope is empty, then default to the scope + // originally requested by the client in the Token Request + // @formatter:off return this.accessibleScopes.isEmpty() + || CollectionUtils.isEmpty(userRequest.getAccessToken().getScopes()) || CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes); + // @formatter:on } return false; } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java index 152f77295a5..013029ba6e9 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java @@ -458,14 +458,36 @@ public void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() { @Test public void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() { OidcUserService userService = new OidcUserService(); - OidcUserRequest request = new OidcUserRequest(TestClientRegistrations.clientRegistration().build(), - TestOAuth2AccessTokens.noScopes(), TestOidcIdTokens.idToken().build()); + OidcUserRequest request = new OidcUserRequest(this.clientRegistrationBuilder.build(), + TestOAuth2AccessTokens.noScopes(), this.idToken); OidcUser user = userService.loadUser(request); assertThat(user.getAuthorities()).hasSize(1); Iterator authorities = user.getAuthorities().iterator(); assertThat(authorities.next()).isInstanceOf(OidcUserAuthority.class); } + @Test + public void loadUserWhenTokenDoesNotContainScopesAndUserInfoUriThenUserInfoRequested() { + // @formatter:off + String userInfoResponse = "{\n" + + " \"sub\": \"subject1\",\n" + + " \"name\": \"first last\",\n" + + " \"given_name\": \"first\",\n" + + " \"family_name\": \"last\",\n" + + " \"preferred_username\": \"user1\",\n" + + " \"email\": \"user1@example.com\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(userInfoResponse)); + String userInfoUri = this.server.url("/user").toString(); + ClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build(); + OidcUserService userService = new OidcUserService(); + OidcUserRequest request = new OidcUserRequest(clientRegistration, TestOAuth2AccessTokens.noScopes(), + this.idToken); + OidcUser user = userService.loadUser(request); + assertThat(user.getUserInfo()).isNotNull(); + } + private MockResponse jsonResponse(String json) { // @formatter:off return new MockResponse() diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java index f952ff4bd50..dbe4e533e6a 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java @@ -16,10 +16,12 @@ package org.springframework.security.oauth2.core.endpoint; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.oidc.OidcScopes; import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; /** @@ -42,7 +44,8 @@ public static OAuth2AccessTokenResponse.Builder accessTokenResponse() { public static OAuth2AccessTokenResponse.Builder oidcAccessTokenResponse() { Map additionalParameters = new HashMap<>(); additionalParameters.put(OidcParameterNames.ID_TOKEN, "id-token"); - return accessTokenResponse().additionalParameters(additionalParameters); + return accessTokenResponse().scopes(Collections.singleton(OidcScopes.OPENID)) + .additionalParameters(additionalParameters); } }