diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/NimbusUserInfoResponseClient.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/NimbusUserInfoResponseClient.java deleted file mode 100644 index c63d905ec2..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/NimbusUserInfoResponseClient.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2002-2018 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.userinfo; - -import com.nimbusds.oauth2.sdk.ErrorObject; -import com.nimbusds.oauth2.sdk.ParseException; -import com.nimbusds.oauth2.sdk.http.HTTPRequest; -import com.nimbusds.oauth2.sdk.http.HTTPResponse; -import com.nimbusds.oauth2.sdk.token.BearerAccessToken; -import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse; -import com.nimbusds.openid.connect.sdk.UserInfoRequest; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.client.AbstractClientHttpResponse; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.http.converter.GenericHttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.util.Assert; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.charset.Charset; - -/** - * NOTE: This is a straight copy of org.springframework.security.oauth2.client.userinfo.NimbusUserInfoResponseClient - * - * @author Joe Grandja - * @since 5.0 - */ -final class NimbusUserInfoResponseClient { - private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response"; - private final GenericHttpMessageConverter genericHttpMessageConverter = new MappingJackson2HttpMessageConverter(); - - T getUserInfoResponse(OAuth2UserRequest userInfoRequest, Class returnType) throws OAuth2AuthenticationException { - ClientHttpResponse userInfoResponse = this.getUserInfoResponse( - userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken()); - try { - return (T) this.genericHttpMessageConverter.read(returnType, userInfoResponse); - } catch (IOException | HttpMessageNotReadableException ex) { - OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, - "An error occurred reading the UserInfo Success response: " + ex.getMessage(), null); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); - } - } - - T getUserInfoResponse(OAuth2UserRequest userInfoRequest, ParameterizedTypeReference typeReference) throws OAuth2AuthenticationException { - ClientHttpResponse userInfoResponse = this.getUserInfoResponse( - userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken()); - try { - return (T) this.genericHttpMessageConverter.read(typeReference.getType(), null, userInfoResponse); - } catch (IOException | HttpMessageNotReadableException ex) { - OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, - "An error occurred reading the UserInfo Success response: " + ex.getMessage(), null); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); - } - } - - private ClientHttpResponse getUserInfoResponse(ClientRegistration clientRegistration, - OAuth2AccessToken oauth2AccessToken) throws OAuth2AuthenticationException { - URI userInfoUri = URI.create(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()); - BearerAccessToken accessToken = new BearerAccessToken(oauth2AccessToken.getTokenValue()); - - UserInfoRequest userInfoRequest = new UserInfoRequest(userInfoUri, accessToken); - HTTPRequest httpRequest = userInfoRequest.toHTTPRequest(); - httpRequest.setAccept(MediaType.APPLICATION_JSON_VALUE); - httpRequest.setConnectTimeout(30000); - httpRequest.setReadTimeout(30000); - HTTPResponse httpResponse; - - try { - httpResponse = httpRequest.send(); - } catch (IOException ex) { - throw new AuthenticationServiceException("An error occurred while sending the UserInfo Request: " + - ex.getMessage(), ex); - } - - if (httpResponse.getStatusCode() == HTTPResponse.SC_OK) { - return new NimbusClientHttpResponse(httpResponse); - } - - UserInfoErrorResponse userInfoErrorResponse; - try { - userInfoErrorResponse = UserInfoErrorResponse.parse(httpResponse); - } catch (ParseException ex) { - OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, - "An error occurred parsing the UserInfo Error response: " + ex.getMessage(), null); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); - } - ErrorObject errorObject = userInfoErrorResponse.getErrorObject(); - - StringBuilder errorDescription = new StringBuilder(); - errorDescription.append("An error occurred while attempting to access the UserInfo Endpoint -> "); - errorDescription.append("Error details: ["); - errorDescription.append("UserInfo Uri: ").append(userInfoUri.toString()); - errorDescription.append(", Http Status: ").append(errorObject.getHTTPStatusCode()); - if (errorObject.getCode() != null) { - errorDescription.append(", Error Code: ").append(errorObject.getCode()); - } - if (errorObject.getDescription() != null) { - errorDescription.append(", Error Description: ").append(errorObject.getDescription()); - } - errorDescription.append("]"); - - OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, errorDescription.toString(), null); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); - } - - private static class NimbusClientHttpResponse extends AbstractClientHttpResponse { - private final HTTPResponse httpResponse; - private final HttpHeaders headers; - - private NimbusClientHttpResponse(HTTPResponse httpResponse) { - Assert.notNull(httpResponse, "httpResponse cannot be null"); - this.httpResponse = httpResponse; - this.headers = new HttpHeaders(); - this.headers.setAll(httpResponse.getHeaders()); - } - - @Override - public int getRawStatusCode() throws IOException { - return this.httpResponse.getStatusCode(); - } - - @Override - public String getStatusText() throws IOException { - return String.valueOf(this.getRawStatusCode()); - } - - @Override - public void close() { - } - - @Override - public InputStream getBody() throws IOException { - InputStream inputStream = new ByteArrayInputStream( - this.httpResponse.getContent().getBytes(Charset.forName("UTF-8"))); - return inputStream; - } - - @Override - public HttpHeaders getHeaders() { - return this.headers; - } - } -} 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 e354608c3d..28eb750fd9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -15,8 +15,9 @@ */ package org.springframework.security.oauth2.client.oidc.userinfo; -import org.springframework.core.ParameterizedTypeReference; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; @@ -26,12 +27,12 @@ import org.springframework.security.oauth2.core.oidc.OidcUserInfo; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; +import org.springframework.security.oauth2.core.user.OAuth2User; 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,17 +50,15 @@ public class OidcUserService implements OAuth2UserService userInfoScopes = new HashSet<>( Arrays.asList(OidcScopes.PROFILE, OidcScopes.EMAIL, OidcScopes.ADDRESS, OidcScopes.PHONE)); - private NimbusUserInfoResponseClient userInfoResponseClient = new NimbusUserInfoResponseClient(); + private final OAuth2UserService defaultUserService = new DefaultOAuth2UserService(); @Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { Assert.notNull(userRequest, "userRequest cannot be null"); OidcUserInfo userInfo = null; if (this.shouldRetrieveUserInfo(userRequest)) { - ParameterizedTypeReference> typeReference = - new ParameterizedTypeReference>() {}; - Map userAttributes = this.userInfoResponseClient.getUserInfoResponse(userRequest, typeReference); - userInfo = new OidcUserInfo(userAttributes); + OAuth2User oauth2User = this.defaultUserService.loadUser(userRequest); + userInfo = new OidcUserInfo(oauth2User.getAttributes()); // http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse // Due to the possibility of token substitution attacks (see Section 16.11), 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 ba08c8163b..7ac59eb954 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 @@ -79,6 +79,8 @@ public class OidcUserServiceTests { when(this.providerDetails.getUserInfoEndpoint()).thenReturn(this.userInfoEndpoint); when(this.clientRegistration.getAuthorizationGrantType()).thenReturn(AuthorizationGrantType.AUTHORIZATION_CODE); + when(this.userInfoEndpoint.getUserNameAttributeName()).thenReturn(StandardClaimNames.SUB); + this.accessToken = mock(OAuth2AccessToken.class); Set authorizedScopes = new LinkedHashSet<>(Arrays.asList(OidcScopes.OPENID, OidcScopes.PROFILE)); when(this.accessToken.getScopes()).thenReturn(authorizedScopes);