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
This commit is contained in:
Édouard Hue 2019-07-10 21:48:18 +02:00 committed by Josh Cummings
parent b153d92b23
commit e8dd1325fd
5 changed files with 39 additions and 346 deletions

View File

@ -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) {

View File

@ -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()) {

View File

@ -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()) {

View File

@ -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_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 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_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))

View File

@ -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_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 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_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))