mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 09:12:14 +00:00
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:
parent
3c240d0ce3
commit
6f5a443175
@ -50,8 +50,14 @@ public class ServerBearerTokenAuthenticationConverter
|
|||||||
private boolean allowUriQueryParameter = false;
|
private boolean allowUriQueryParameter = false;
|
||||||
|
|
||||||
public Mono<Authentication> convert(ServerWebExchange exchange) {
|
public Mono<Authentication> convert(ServerWebExchange exchange) {
|
||||||
return Mono.justOrEmpty(this.token(exchange.getRequest()))
|
return Mono.justOrEmpty(token(exchange.getRequest()))
|
||||||
.map(BearerTokenAuthenticationToken::new);
|
.map(token -> {
|
||||||
|
if (token.isEmpty()) {
|
||||||
|
BearerTokenError error = invalidTokenError();
|
||||||
|
throw new OAuth2AuthenticationException(error);
|
||||||
|
}
|
||||||
|
return new BearerTokenAuthenticationToken(token);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private String token(ServerHttpRequest request) {
|
private String token(ServerHttpRequest request) {
|
||||||
@ -90,11 +96,8 @@ public class ServerBearerTokenAuthenticationConverter
|
|||||||
if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
|
if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
|
||||||
Matcher matcher = authorizationPattern.matcher(authorization);
|
Matcher matcher = authorizationPattern.matcher(authorization);
|
||||||
|
|
||||||
if ( !matcher.matches() ) {
|
if (!matcher.matches() ) {
|
||||||
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
|
BearerTokenError error = invalidTokenError();
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Bearer token is malformed",
|
|
||||||
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
|
||||||
throw new OAuth2AuthenticationException(error);
|
throw new OAuth2AuthenticationException(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +106,13 @@ public class ServerBearerTokenAuthenticationConverter
|
|||||||
return null;
|
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) {
|
private boolean isParameterTokenSupportedForRequest(ServerHttpRequest request) {
|
||||||
return this.allowUriQueryParameter && HttpMethod.GET.equals(request.getMethod());
|
return this.allowUriQueryParameter && HttpMethod.GET.equals(request.getMethod());
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,19 @@ package org.springframework.security.oauth2.server.resource.web.server;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
|
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 java.util.Base64;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
import static org.assertj.core.api.Assertions.catchThrowableOfType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
@ -52,6 +56,21 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
|||||||
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
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
|
@Test
|
||||||
public void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {
|
public void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
|
||||||
@ -123,6 +142,23 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
|||||||
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
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
|
@Test
|
||||||
public void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {
|
public void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
|
||||||
|
Loading…
x
Reference in New Issue
Block a user