ServerBearerTokenAuthenticationConverter Handles Empty Tokens

Previously ServerBearerTokenAuthenticationConverter would throw an
IllegalArgumentException when the access token in a URI was empty String.
It also incorrectly provided HttpStatus.BAD_REQUEST for an empty String
access token in the headers.

This changes ServerBearerTokenAuthenticationConverter to consistently
throw a OAuth2AuthenticationException with an HttpStatus.UNAUTHORIZED

Fixes gh-7011
This commit is contained in:
Rob Winch 2019-06-19 09:14:57 -05:00 committed by Josh Cummings
parent 3c240d0ce3
commit 6f5a443175
2 changed files with 53 additions and 7 deletions

View File

@ -50,8 +50,14 @@ public class ServerBearerTokenAuthenticationConverter
private boolean allowUriQueryParameter = false;
public Mono<Authentication> convert(ServerWebExchange exchange) {
return Mono.justOrEmpty(this.token(exchange.getRequest()))
.map(BearerTokenAuthenticationToken::new);
return Mono.justOrEmpty(token(exchange.getRequest()))
.map(token -> {
if (token.isEmpty()) {
BearerTokenError error = invalidTokenError();
throw new OAuth2AuthenticationException(error);
}
return new BearerTokenAuthenticationToken(token);
});
}
private String token(ServerHttpRequest request) {
@ -90,11 +96,8 @@ public class ServerBearerTokenAuthenticationConverter
if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
Matcher matcher = authorizationPattern.matcher(authorization);
if ( !matcher.matches() ) {
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
HttpStatus.BAD_REQUEST,
"Bearer token is malformed",
"https://tools.ietf.org/html/rfc6750#section-3.1");
if (!matcher.matches() ) {
BearerTokenError error = invalidTokenError();
throw new OAuth2AuthenticationException(error);
}
@ -103,6 +106,13 @@ public class ServerBearerTokenAuthenticationConverter
return null;
}
private static BearerTokenError invalidTokenError() {
return new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
HttpStatus.UNAUTHORIZED,
"Bearer token is malformed",
"https://tools.ietf.org/html/rfc6750#section-3.1");
}
private boolean isParameterTokenSupportedForRequest(ServerHttpRequest request) {
return this.allowUriQueryParameter && HttpMethod.GET.equals(request.getMethod());
}

View File

@ -19,15 +19,19 @@ package org.springframework.security.oauth2.server.resource.web.server;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
import java.util.Base64;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.catchThrowableOfType;
/**
* @author Rob Winch
@ -52,6 +56,21 @@ public class ServerBearerTokenAuthenticationConverterTests {
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
}
// gh-7011
@Test
public void resolveWhenValidHeaderIsEmptyStringThenTokenIsResolved() {
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
.get("/")
.header(HttpHeaders.AUTHORIZATION, "Bearer ");
OAuth2AuthenticationException expected = catchThrowableOfType(() -> convertToToken(request),
OAuth2AuthenticationException.class);
BearerTokenError error = (BearerTokenError) expected.getError();
assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
public void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
@ -123,6 +142,23 @@ public class ServerBearerTokenAuthenticationConverterTests {
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
}
// gh-7011
@Test
public void resolveWhenQueryParameterIsEmptyAndSupportedThenOAuth2AuthenticationException() {
this.converter.setAllowUriQueryParameter(true);
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
.get("/")
.queryParam("access_token", "");
OAuth2AuthenticationException expected = catchThrowableOfType(() -> convertToToken(request),
OAuth2AuthenticationException.class);
BearerTokenError error = (BearerTokenError) expected.getError();
assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
public void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest