diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java index eaa2f3ecaae..7505100c81a 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java @@ -42,7 +42,7 @@ public final class DefaultBearerTokenResolver implements BearerTokenResolver { private static final String ACCESS_TOKEN_PARAMETER_NAME = "access_token"; - private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?[a-zA-Z0-9-._~+/]+=*)$", + private static final Pattern authorizationPattern = Pattern.compile("^Bearer +(?[a-zA-Z0-9-._~+/]+=*)$", Pattern.CASE_INSENSITIVE); private boolean allowFormEncodedBodyParameter = false; diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverter.java index b3cc10f7300..27991f4c3ba 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverter.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverter.java @@ -52,7 +52,7 @@ public class ServerBearerTokenAuthenticationConverter implements ServerAuthentic private static final String ACCESS_TOKEN_PARAMETER_NAME = "access_token"; - private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?[a-zA-Z0-9-._~+/]+=*)$", + private static final Pattern authorizationPattern = Pattern.compile("^Bearer +(?[a-zA-Z0-9-._~+/]+=*)$", Pattern.CASE_INSENSITIVE); private boolean allowFormEncodedBodyParameter = false; diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java index f9e317119b7..c21c07a97c3 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java @@ -55,6 +55,39 @@ public void resolveWhenValidHeaderIsPresentThenTokenIsResolved() { assertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN); } + // gh-19500 + @Test + public void resolveWhenValidHeaderWithOneSpaceIsPresentThenTokenIsResolved() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Bearer " + TEST_TOKEN); + assertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN); + } + + // gh-19500 + @Test + public void resolveWhenValidHeaderWithTwoSpacesIsPresentThenTokenIsResolved() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Bearer " + TEST_TOKEN); + assertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN); + } + + // gh-19500 + @Test + public void resolveWhenValidHeaderWithThreeSpacesIsPresentThenTokenIsResolved() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Bearer " + TEST_TOKEN); + assertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN); + } + + // gh-19500 + @Test + public void resolveWhenHeaderWithZeroSpacesIsPresentThenTokenIsNotResolved() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Bearer" + TEST_TOKEN); + assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request)) + .withMessageContaining(("Bearer token is malformed")); + } + // gh-8502 @Test public void resolveWhenHeaderEndsWithPaddingIndicatorThenTokenIsResolved() { diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverterTests.java index cb73858c7ca..34d730216e4 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverterTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverterTests.java @@ -60,6 +60,40 @@ public void resolveWhenValidHeaderIsPresentThenTokenIsResolved() { assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN); } + // gh-19500 + @Test + public void resolveWhenValidHeaderWithOneSpaceIsPresentThenTokenIsResolved() { + MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest.get("/") + .header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_TOKEN); + assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN); + } + + // gh-19500 + @Test + public void resolveWhenValidHeaderWithTwoSpacesIsPresentThenTokenIsResolved() { + MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest.get("/") + .header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_TOKEN); + assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN); + } + + // gh-19500 + @Test + public void resolveWhenValidHeaderWithThreeSpacesIsPresentThenTokenIsResolved() { + MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest.get("/") + .header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_TOKEN); + assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN); + } + + // gh-19500 + @Test + public void resolveWhenHeaderWithZeroSpacesIsPresentThenTokenIsNotResolved() { + MockServerHttpRequest.BaseBuilder request = MockServerHttpRequest.get("/") + .header(HttpHeaders.AUTHORIZATION, "Bearer" + TEST_TOKEN); + assertThatExceptionOfType(OAuth2AuthenticationException.class) + .isThrownBy(() -> convertToToken(request)) + .withMessageContaining("Bearer token is malformed"); + } + // gh-8502 @Test public void resolveWhenHeaderEndsWithPaddingIndicatorThenTokenIsResolved() {