Sfoglia il codice sorgente

Fixed misleading OAuth2 error messages

Error messages sent by BearerTokenAccessDeniedHandler included
information about the scopes of the rejected token instead of
the scopes required by the resource.
* Removal of token scopes from error_description attribute.
* Removal of scope attribute from WWW-Authenticate response header.

Fixes gh-7089
Édouard Hue 6 anni fa
parent
commit
e8dd1325fd

+ 8 - 10
config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java

@@ -108,7 +108,6 @@ import org.springframework.test.web.servlet.ResultMatcher;
 import org.springframework.test.web.servlet.request.RequestPostProcessor;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
-import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -397,7 +396,7 @@ public class OAuth2ResourceServerConfigurerTests {
 		this.mvc.perform(get("/requires-read-scope")
 				.with(bearerToken(token)))
 				.andExpect(status().isForbidden())
-				.andExpect(insufficientScopeHeader(""));
+				.andExpect(insufficientScopeHeader());
 	}
 
 	@Test
@@ -411,7 +410,7 @@ public class OAuth2ResourceServerConfigurerTests {
 		this.mvc.perform(get("/requires-read-scope")
 				.with(bearerToken(token)))
 				.andExpect(status().isForbidden())
-				.andExpect(insufficientScopeHeader("message:write"));
+				.andExpect(insufficientScopeHeader());
 	}
 
 	@Test
@@ -497,7 +496,7 @@ public class OAuth2ResourceServerConfigurerTests {
 		this.mvc.perform(get("/ms-requires-read-scope")
 				.with(bearerToken(token)))
 				.andExpect(status().isForbidden())
-				.andExpect(insufficientScopeHeader(""));
+				.andExpect(insufficientScopeHeader());
 
 	}
 
@@ -512,7 +511,7 @@ public class OAuth2ResourceServerConfigurerTests {
 		this.mvc.perform(get("/ms-requires-read-scope")
 				.with(bearerToken(token)))
 				.andExpect(status().isForbidden())
-				.andExpect(insufficientScopeHeader("message:write"));
+				.andExpect(insufficientScopeHeader());
 	}
 
 	@Test
@@ -526,7 +525,7 @@ public class OAuth2ResourceServerConfigurerTests {
 		this.mvc.perform(get("/ms-deny")
 				.with(bearerToken(token)))
 				.andExpect(status().isForbidden())
-				.andExpect(insufficientScopeHeader("message:read"));
+				.andExpect(insufficientScopeHeader());
 	}
 
 	// -- Resource Server should not engage csrf
@@ -2230,12 +2229,11 @@ public class OAuth2ResourceServerConfigurerTests {
 		);
 	}
 
-	private static ResultMatcher insufficientScopeHeader(String scope) {
+	private static ResultMatcher insufficientScopeHeader() {
 		return header().string(HttpHeaders.WWW_AUTHENTICATE, "Bearer " +
 				"error=\"insufficient_scope\"" +
-				", error_description=\"The token provided has insufficient scope [" + scope + "] for this request\"" +
-				", error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"" +
-				(StringUtils.hasText(scope) ? ", scope=\"" + scope + "\"" : ""));
+				", error_description=\"The request requires higher privileges than provided by the access token.\"" +
+				", error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"");
 	}
 
 	private void mockWebServer(String response) {

+ 9 - 44
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandler.java

@@ -16,16 +16,6 @@
 
 package org.springframework.security.oauth2.server.resource.web.access;
 
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.stream.Collectors;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.AccessDeniedException;
@@ -33,7 +23,14 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
 import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
 import org.springframework.security.web.access.AccessDeniedHandler;
-import org.springframework.util.StringUtils;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * Translates any {@link AccessDeniedException} into an HTTP response in accordance with
@@ -48,9 +45,6 @@ import org.springframework.util.StringUtils;
  */
 public final class BearerTokenAccessDeniedHandler implements AccessDeniedHandler {
 
-	private static final Collection<String> WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES =
-			Arrays.asList("scope", "scp");
-
 	private String realmName;
 
 	/**
@@ -75,19 +69,9 @@ public final class BearerTokenAccessDeniedHandler implements AccessDeniedHandler
 		}
 
 		if (request.getUserPrincipal() instanceof AbstractOAuth2TokenAuthenticationToken) {
-			AbstractOAuth2TokenAuthenticationToken token =
-					(AbstractOAuth2TokenAuthenticationToken) request.getUserPrincipal();
-
-			String scope = getScope(token);
-
 			parameters.put("error", BearerTokenErrorCodes.INSUFFICIENT_SCOPE);
-			parameters.put("error_description",
-					String.format("The token provided has insufficient scope [%s] for this request", scope));
+			parameters.put("error_description", "The request requires higher privileges than provided by the access token.");
 			parameters.put("error_uri", "https://tools.ietf.org/html/rfc6750#section-3.1");
-
-			if (StringUtils.hasText(scope)) {
-				parameters.put("scope", scope);
-			}
 		}
 
 		String wwwAuthenticate = computeWWWAuthenticateHeaderValue(parameters);
@@ -105,25 +89,6 @@ public final class BearerTokenAccessDeniedHandler implements AccessDeniedHandler
 		this.realmName = realmName;
 	}
 
-	private static String getScope(AbstractOAuth2TokenAuthenticationToken token) {
-
-		Map<String, Object> attributes = token.getTokenAttributes();
-
-		for (String attributeName : WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES) {
-			Object scopes = attributes.get(attributeName);
-			if (scopes instanceof String) {
-				return (String) scopes;
-			} else if (scopes instanceof Collection) {
-				Collection coll = (Collection) scopes;
-				return (String) coll.stream()
-						.map(String::valueOf)
-						.collect(Collectors.joining(" "));
-			}
-		}
-
-		return "";
-	}
-
 	private static String computeWWWAuthenticateHeaderValue(Map<String, String> parameters) {
 		String wwwAuthenticate = "Bearer";
 		if (!parameters.isEmpty()) {

+ 10 - 42
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandler.java

@@ -16,14 +16,6 @@
 
 package org.springframework.security.oauth2.server.resource.web.access.server;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import reactor.core.publisher.Mono;
-
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.AccessDeniedException;
@@ -31,8 +23,14 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
 import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
 import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
-import org.springframework.util.StringUtils;
 import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * Translates any {@link AccessDeniedException} into an HTTP response in accordance with
@@ -63,8 +61,7 @@ public class BearerTokenServerAccessDeniedHandler implements ServerAccessDeniedH
 
 		return exchange.getPrincipal()
 				.filter(AbstractOAuth2TokenAuthenticationToken.class::isInstance)
-				.cast(AbstractOAuth2TokenAuthenticationToken.class)
-				.map(token -> errorMessageParameters(token, parameters))
+				.map(token -> errorMessageParameters(parameters))
 				.switchIfEmpty(Mono.just(parameters))
 				.flatMap(params -> respond(exchange, params));
 	}
@@ -78,21 +75,11 @@ public class BearerTokenServerAccessDeniedHandler implements ServerAccessDeniedH
 		this.realmName = realmName;
 	}
 
-	private static Map<String, String> errorMessageParameters(
-			AbstractOAuth2TokenAuthenticationToken token,
-			Map<String, String> parameters) {
-
-		String scope = getScope(token);
-
+	private static Map<String, String> errorMessageParameters(Map<String, String> parameters) {
 		parameters.put("error", BearerTokenErrorCodes.INSUFFICIENT_SCOPE);
-		parameters.put("error_description",
-				String.format("The token provided has insufficient scope [%s] for this request", scope));
+		parameters.put("error_description", "The request requires higher privileges than provided by the access token.");
 		parameters.put("error_uri", "https://tools.ietf.org/html/rfc6750#section-3.1");
 
-		if (StringUtils.hasText(scope)) {
-			parameters.put("scope", scope);
-		}
-
 		return parameters;
 	}
 
@@ -103,25 +90,6 @@ public class BearerTokenServerAccessDeniedHandler implements ServerAccessDeniedH
 		return exchange.getResponse().setComplete();
 	}
 
-	private static String getScope(AbstractOAuth2TokenAuthenticationToken token) {
-
-		Map<String, Object> attributes = token.getTokenAttributes();
-
-		for (String attributeName : WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES) {
-			Object scopes = attributes.get(attributeName);
-			if (scopes instanceof String) {
-				return (String) scopes;
-			} else if (scopes instanceof Collection) {
-				Collection coll = (Collection) scopes;
-				return (String) coll.stream()
-						.map(String::valueOf)
-						.collect(Collectors.joining(" "));
-			}
-		}
-
-		return "";
-	}
-
 	private static String computeWWWAuthenticateHeaderValue(Map<String, String> parameters) {
 		String wwwAuthenticate = "Bearer";
 		if (!parameters.isEmpty()) {

+ 5 - 130
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandlerTests.java

@@ -16,14 +16,8 @@
 
 package org.springframework.security.oauth2.server.resource.web.access;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Map;
-
-import org.assertj.core.util.Maps;
 import org.junit.Before;
 import org.junit.Test;
-
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.security.authentication.TestingAuthenticationToken;
@@ -31,6 +25,9 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.core.AbstractOAuth2Token;
 import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
 
+import java.util.Collections;
+import java.util.Map;
+
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 
@@ -81,7 +78,7 @@ public class BearerTokenAccessDeniedHandlerTests {
 	}
 
 	@Test
-	public void handleWhenTokenHasNoScopesThenInsufficientScopeError()
+	public void handleWhenOAuth2AuthenticatedThenStatus403AndAuthHeaderWithInsufficientScopeErrorAttribute()
 			throws Exception {
 
 		MockHttpServletRequest request = new MockHttpServletRequest();
@@ -94,132 +91,10 @@ public class BearerTokenAccessDeniedHandlerTests {
 
 		assertThat(response.getStatus()).isEqualTo(403);
 		assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"");
-	}
-
-
-	@Test
-	public void handleWhenTokenHasScopeAttributeThenInsufficientScopeErrorWithScopes()
-			throws Exception {
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		Map<String, Object> attributes = Maps.newHashMap("scope", "message:read message:write");
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		request.setUserPrincipal(token);
-
-		this.accessDeniedHandler.handle(request, response, null);
-
-		assertThat(response.getStatus()).isEqualTo(403);
-		assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [message:read message:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"message:read message:write\"");
-	}
-
-	@Test
-	public void handleWhenTokenHasEmptyScopeAttributeThenInsufficientScopeError()
-			throws Exception {
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		Map<String, Object> attributes = Maps.newHashMap("scope", "");
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		request.setUserPrincipal(token);
-
-		this.accessDeniedHandler.handle(request, response, null);
-
-		assertThat(response.getStatus()).isEqualTo(403);
-		assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"");
-	}
-
-	@Test
-	public void handleWhenTokenHasScpAttributeThenInsufficientScopeErrorWithScopes()
-			throws Exception {
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		Map<String, Object> attributes = Maps.newHashMap("scp", Arrays.asList("message:read", "message:write"));
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		request.setUserPrincipal(token);
-
-		this.accessDeniedHandler.handle(request, response, null);
-
-		assertThat(response.getStatus()).isEqualTo(403);
-		assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [message:read message:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"message:read message:write\"");
-	}
-
-	@Test
-	public void handleWhenTokenHasEmptyScpAttributeThenInsufficientScopeError()
-			throws Exception {
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		Map<String, Object> attributes = Maps.newHashMap("scp", Collections.emptyList());
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		request.setUserPrincipal(token);
-
-		this.accessDeniedHandler.handle(request, response, null);
-
-		assertThat(response.getStatus()).isEqualTo(403);
-		assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [] for this request\", " +
+				"error_description=\"The request requires higher privileges than provided by the access token.\", " +
 				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"");
 	}
 
-	@Test
-	public void handleWhenTokenHasBothScopeAndScpAttributesTheInsufficientErrorBasedOnScopeAttribute()
-			throws Exception {
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		Map<String, Object> attributes = Maps.newHashMap("scp", Arrays.asList("message:read", "message:write"));
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		request.setUserPrincipal(token);
-		attributes.put("scope", "missive:read missive:write");
-
-		this.accessDeniedHandler.handle(request, response, null);
-
-		assertThat(response.getStatus()).isEqualTo(403);
-		assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [missive:read missive:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"missive:read missive:write\"");
-	}
-
-	@Test
-	public void handleWhenTokenHasScopeAttributeAndRealmIsSetThenInsufficientScopeErrorWithScopesAndRealm()
-			throws Exception {
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		Map<String, Object> attributes = Maps.newHashMap("scope", "message:read message:write");
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		request.setUserPrincipal(token);
-
-		this.accessDeniedHandler.setRealmName("test");
-		this.accessDeniedHandler.handle(request, response, null);
-
-		assertThat(response.getStatus()).isEqualTo(403);
-		assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer realm=\"test\", " +
-				"error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [message:read message:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"message:read message:write\"");
-	}
-
 	@Test
 	public void setRealmNameWhenNullRealmNameThenNoExceptionThrown() {
 		assertThatCode(() -> this.accessDeniedHandler.setRealmName(null))

+ 7 - 120
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandlerTests.java

@@ -16,15 +16,8 @@
 
 package org.springframework.security.oauth2.server.resource.web.access.server;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Map;
-
-import org.assertj.core.util.Maps;
 import org.junit.Before;
 import org.junit.Test;
-import reactor.core.publisher.Mono;
-
 import org.springframework.http.HttpStatus;
 import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
 import org.springframework.security.authentication.TestingAuthenticationToken;
@@ -32,6 +25,11 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.core.AbstractOAuth2Token;
 import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
 import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
@@ -78,7 +76,7 @@ public class BearerTokenServerAccessDeniedHandlerTests {
 	}
 
 	@Test
-	public void handleWhenTokenHasNoScopesThenInsufficientScopeError() {
+	public void handleWhenOAuth2AuthenticatedThenStatus403AndAuthHeaderWithInsufficientScopeErrorAttribute() {
 
 		Authentication token = new TestingOAuth2TokenAuthenticationToken(Collections.emptyMap());
 		ServerWebExchange exchange = mock(ServerWebExchange.class);
@@ -90,121 +88,10 @@ public class BearerTokenServerAccessDeniedHandlerTests {
 		assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
 		assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate")).isEqualTo(
 				Arrays.asList("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\""));
-	}
-
-
-	@Test
-	public void handleWhenTokenHasScopeAttributeThenInsufficientScopeErrorWithScopes() {
-		Map<String, Object> attributes = Maps.newHashMap("scope", "message:read message:write");
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		ServerWebExchange exchange = mock(ServerWebExchange.class);
-		when(exchange.getPrincipal()).thenReturn(Mono.just(token));
-		when(exchange.getResponse()).thenReturn(new MockServerHttpResponse());
-
-		this.accessDeniedHandler.handle(exchange, null).block();
-
-		assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
-		assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate")).isEqualTo(
-				Arrays.asList("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [message:read message:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"message:read message:write\""));
-	}
-
-	@Test
-	public void handleWhenTokenHasEmptyScopeAttributeThenInsufficientScopeError() {
-		Map<String, Object> attributes = Maps.newHashMap("scope", "");
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		ServerWebExchange exchange = mock(ServerWebExchange.class);
-		when(exchange.getPrincipal()).thenReturn(Mono.just(token));
-		when(exchange.getResponse()).thenReturn(new MockServerHttpResponse());
-
-		this.accessDeniedHandler.handle(exchange, null).block();
-
-		assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
-		assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate")).isEqualTo(
-				Arrays.asList("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\""));
-	}
-
-	@Test
-	public void handleWhenTokenHasScpAttributeThenInsufficientScopeErrorWithScopes() {
-		Map<String, Object> attributes = Maps.newHashMap("scp", Arrays.asList("message:read", "message:write"));
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		ServerWebExchange exchange = mock(ServerWebExchange.class);
-		when(exchange.getPrincipal()).thenReturn(Mono.just(token));
-		when(exchange.getResponse()).thenReturn(new MockServerHttpResponse());
-
-		this.accessDeniedHandler.handle(exchange, null).block();
-
-		assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
-		assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate")).isEqualTo(
-				Arrays.asList("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [message:read message:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"message:read message:write\""));
-	}
-
-	@Test
-	public void handleWhenTokenHasEmptyScpAttributeThenInsufficientScopeError() {
-
-		Map<String, Object> attributes = Maps.newHashMap("scp", Collections.emptyList());
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		ServerWebExchange exchange = mock(ServerWebExchange.class);
-		when(exchange.getPrincipal()).thenReturn(Mono.just(token));
-		when(exchange.getResponse()).thenReturn(new MockServerHttpResponse());
-
-		this.accessDeniedHandler.handle(exchange, null).block();
-
-		assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
-		assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate")).isEqualTo(
-				Arrays.asList("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [] for this request\", " +
+				"error_description=\"The request requires higher privileges than provided by the access token.\", " +
 				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\""));
 	}
 
-	@Test
-	public void handleWhenTokenHasBothScopeAndScpAttributesTheInsufficientErrorBasedOnScopeAttribute() {
-		Map<String, Object> attributes = Maps.newHashMap("scp", Arrays.asList("message:read", "message:write"));
-		attributes.put("scope", "missive:read missive:write");
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		ServerWebExchange exchange = mock(ServerWebExchange.class);
-		when(exchange.getPrincipal()).thenReturn(Mono.just(token));
-		when(exchange.getResponse()).thenReturn(new MockServerHttpResponse());
-
-		this.accessDeniedHandler.handle(exchange, null).block();
-
-		assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
-		assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate")).isEqualTo(
-				Arrays.asList("Bearer error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [missive:read missive:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"missive:read missive:write\""));
-	}
-
-	@Test
-	public void handleWhenTokenHasScopeAttributeAndRealmIsSetThenInsufficientScopeErrorWithScopesAndRealm() {
-		Map<String, Object> attributes = Maps.newHashMap("scope", "message:read message:write");
-		Authentication token = new TestingOAuth2TokenAuthenticationToken(attributes);
-		ServerWebExchange exchange = mock(ServerWebExchange.class);
-		when(exchange.getPrincipal()).thenReturn(Mono.just(token));
-		when(exchange.getResponse()).thenReturn(new MockServerHttpResponse());
-
-		this.accessDeniedHandler.setRealmName("test");
-		this.accessDeniedHandler.handle(exchange, null).block();
-
-		assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
-		assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate"))
-				.isEqualTo(Arrays.asList("Bearer realm=\"test\", " +
-				"error=\"insufficient_scope\", " +
-				"error_description=\"The token provided has insufficient scope [message:read message:write] for this request\", " +
-				"error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\", " +
-				"scope=\"message:read message:write\""));
-	}
-
 	@Test
 	public void setRealmNameWhenNullRealmNameThenNoExceptionThrown() {
 		assertThatCode(() -> this.accessDeniedHandler.setRealmName(null))