| 
					
				 | 
			
			
				@@ -0,0 +1,165 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Copyright 2002-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.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.client.AbstractClientHttpResponse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.http.client.ClientHttpResponse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.http.converter.GenericHttpMessageConverter; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, Class<T> returnType) throws OAuth2AuthenticationException { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ClientHttpResponse userInfoResponse = this.getUserInfoResponse( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return (T) this.genericHttpMessageConverter.read(returnType, userInfoResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} catch (IOException 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> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, ParameterizedTypeReference<T> typeReference) throws OAuth2AuthenticationException { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ClientHttpResponse userInfoResponse = this.getUserInfoResponse( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return (T) this.genericHttpMessageConverter.read(typeReference.getType(), null, userInfoResponse); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} catch (IOException 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.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; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |