diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java
index dca6859402..a6ec8d1312 100644
--- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java
+++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java
@@ -15,11 +15,15 @@
*/
package org.springframework.security.oauth2.client.http;
+import com.nimbusds.oauth2.sdk.token.BearerTokenError;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
+import org.springframework.util.StringUtils;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler;
@@ -44,10 +48,39 @@ public class OAuth2ErrorResponseErrorHandler implements ResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
- if (HttpStatus.BAD_REQUEST.equals(response.getStatusCode())) {
- OAuth2Error oauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response);
- throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
+ if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode())) {
+ this.defaultErrorHandler.handleError(response);
}
- this.defaultErrorHandler.handleError(response);
+
+ // A Bearer Token Error may be in the WWW-Authenticate response header
+ // See https://tools.ietf.org/html/rfc6750#section-3
+ OAuth2Error oauth2Error = this.readErrorFromWwwAuthenticate(response.getHeaders());
+ if (oauth2Error == null) {
+ oauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response);
+ }
+
+ throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
+ }
+
+ private OAuth2Error readErrorFromWwwAuthenticate(HttpHeaders headers) {
+ String wwwAuthenticateHeader = headers.getFirst(HttpHeaders.WWW_AUTHENTICATE);
+ if (!StringUtils.hasText(wwwAuthenticateHeader)) {
+ return null;
+ }
+
+ BearerTokenError bearerTokenError;
+ try {
+ bearerTokenError = BearerTokenError.parse(wwwAuthenticateHeader);
+ } catch (Exception ex) {
+ return null;
+ }
+
+ String errorCode = bearerTokenError.getCode() != null ?
+ bearerTokenError.getCode() : OAuth2ErrorCodes.SERVER_ERROR;
+ String errorDescription = bearerTokenError.getDescription();
+ String errorUri = bearerTokenError.getURI() != null ?
+ bearerTokenError.getURI().toString() : null;
+
+ return new OAuth2Error(errorCode, errorDescription, errorUri);
}
}
diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java
index 8d211c000d..6a97b7f449 100644
--- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java
+++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.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.
@@ -16,7 +16,12 @@
package org.springframework.security.oauth2.client.userinfo;
import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
@@ -24,8 +29,12 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
+import org.springframework.web.client.ResponseErrorHandler;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -34,7 +43,7 @@ import java.util.Set;
*
* For standard OAuth 2.0 Provider's, the attribute name used to access the user's name
* from the UserInfo response is required and therefore must be available via
- * {@link org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails.UserInfoEndpoint#getUserNameAttributeName() UserInfoEndpoint.getUserNameAttributeName()}.
+ * {@link ClientRegistration.ProviderDetails.UserInfoEndpoint#getUserNameAttributeName() UserInfoEndpoint.getUserNameAttributeName()}.
*
* NOTE: Attribute names are not standardized between providers and therefore will vary.
* Please consult the provider's API documentation for the set of supported user attribute names.
@@ -48,8 +57,23 @@ import java.util.Set;
*/
public class DefaultOAuth2UserService implements OAuth2UserService {
private static final String MISSING_USER_INFO_URI_ERROR_CODE = "missing_user_info_uri";
+
private static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = "missing_user_name_attribute";
- private NimbusUserInfoResponseClient userInfoResponseClient = new NimbusUserInfoResponseClient();
+
+ private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response";
+
+ private static final ParameterizedTypeReference