From 4c6fef82b9b8d2a985305bcd1c0d59cc60245583 Mon Sep 17 00:00:00 2001
From: Hyeongi Jeong <jacknie8407@gmail.com>
Date: Tue, 15 Oct 2024 16:21:01 +0900
Subject: [PATCH] Fix error when Bearer token is requested with empty string

Issue gh-15885
---
 .../web/DefaultBearerTokenResolver.java       |  5 +++
 ...verBearerTokenAuthenticationConverter.java |  5 +++
 .../web/DefaultBearerTokenResolverTests.java  | 34 +++++++++++++++++++
 ...arerTokenAuthenticationConverterTests.java |  4 +--
 4 files changed, 46 insertions(+), 2 deletions(-)

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 da357ca9c9..1d61e11b0f 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
@@ -64,6 +64,11 @@ public final class DefaultBearerTokenResolver implements BearerTokenResolver {
 			return authorizationHeaderToken;
 		}
 		if (parameterToken != null && isParameterTokenEnabledForRequest(request)) {
+			if (!StringUtils.hasText(parameterToken)) {
+				final BearerTokenError error = BearerTokenErrors
+					.invalidRequest("The requested token parameter is an empty string");
+				throw new OAuth2AuthenticationException(error);
+			}
 			return parameterToken;
 		}
 		return null;
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 d30cd8e05a..4f8d33c6d0 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
@@ -78,6 +78,11 @@ public class ServerBearerTokenAuthenticationConverter implements ServerAuthentic
 			return authorizationHeaderToken;
 		}
 		if (parameterToken != null && isParameterTokenSupportedForRequest(request)) {
+			if (!StringUtils.hasText(parameterToken)) {
+				final BearerTokenError error = BearerTokenErrors
+					.invalidRequest("The requested token parameter is an empty string");
+				throw new OAuth2AuthenticationException(error);
+			}
 			return parameterToken;
 		}
 		return null;
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 e5cfca01c3..7937a699fd 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
@@ -21,8 +21,11 @@ import java.util.Base64;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import org.springframework.http.HttpStatus;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.server.resource.BearerTokenError;
+import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -258,4 +261,35 @@ public class DefaultBearerTokenResolverTests {
 		assertThat(this.resolver.resolve(request)).isNull();
 	}
 
+	@Test
+	public void resolveWhenQueryParameterIsPresentAndEmptyStringThenTokenIsNotResolved() {
+		this.resolver.setAllowUriQueryParameter(true);
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.setMethod("GET");
+		request.addParameter("access_token", "");
+		assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))
+			.withMessageContaining("The requested token parameter is an empty string")
+			.satisfies((e) -> {
+				BearerTokenError error = (BearerTokenError) e.getError();
+				assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);
+				assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);
+			});
+	}
+
+	@Test
+	public void resolveWhenFormParameterIsPresentAndEmptyStringThenTokenIsNotResolved() {
+		this.resolver.setAllowFormEncodedBodyParameter(true);
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.setMethod("POST");
+		request.setContentType("application/x-www-form-urlencoded");
+		request.addParameter("access_token", "");
+		assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))
+			.withMessageContaining("The requested token parameter is an empty string")
+			.satisfies((e) -> {
+				BearerTokenError error = (BearerTokenError) e.getError();
+				assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);
+				assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);
+			});
+	}
+
 }
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 6d9c7a5b98..b43329fc97 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
@@ -187,9 +187,9 @@ public class ServerBearerTokenAuthenticationConverterTests {
 				.isThrownBy(() -> convertToToken(request))
 				.satisfies((ex) -> {
 					BearerTokenError error = (BearerTokenError) ex.getError();
-					assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
+					assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);
 					assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
-					assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
+					assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);
 				});
 		// @formatter:on
 	}