From 4957c5a7e967e2d3ddbc22def7d20cef07605300 Mon Sep 17 00:00:00 2001 From: Daniel Garnier-Moiroux Date: Fri, 23 Jan 2026 17:28:45 +0100 Subject: [PATCH] Add BearerTokenAuthenticationEntryPoint#setResourceMetadataParameterResolver Closes gh-18542 Signed-off-by: Daniel Garnier-Moiroux --- .../BearerTokenAuthenticationEntryPoint.java | 17 ++++++++++++++++- ...earerTokenAuthenticationEntryPointTests.java | 13 +++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java index 76607173cc..c7640c2a14 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java @@ -18,6 +18,7 @@ package org.springframework.security.oauth2.server.resource.web; import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Function; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -31,6 +32,7 @@ import org.springframework.security.oauth2.server.resource.BearerTokenError; import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.util.UrlUtils; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponentsBuilder; @@ -51,6 +53,8 @@ public final class BearerTokenAuthenticationEntryPoint implements Authentication private String realmName; + private Function resourceMetadataParameterResolver = BearerTokenAuthenticationEntryPoint::getResourceMetadataParameter; + /** * Collect error details from the provided parameters and format according to RFC * 6750, specifically {@code error}, {@code error_description}, {@code error_uri}, and @@ -83,7 +87,7 @@ public final class BearerTokenAuthenticationEntryPoint implements Authentication status = bearerTokenError.getHttpStatus(); } } - parameters.put("resource_metadata", getResourceMetadataParameter(request)); + parameters.put("resource_metadata", this.resourceMetadataParameterResolver.apply(request)); String wwwAuthenticate = computeWWWAuthenticateHeaderValue(parameters); response.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate); response.setStatus(status.value()); @@ -97,6 +101,17 @@ public final class BearerTokenAuthenticationEntryPoint implements Authentication this.realmName = realmName; } + /** + * Set the resolver to compute the {@code resource_metadata} parameter from the + * request. + * @param resourceMetadataParameterResolver + */ + public void setResourceMetadataParameterResolver( + Function resourceMetadataParameterResolver) { + Assert.notNull(resourceMetadataParameterResolver, "resourceMetadataParameterResolver cannot be null"); + this.resourceMetadataParameterResolver = resourceMetadataParameterResolver; + } + private static String getResourceMetadataParameter(HttpServletRequest request) { String path = request.getContextPath() + OAuth2ProtectedResourceMetadataFilter.DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI; diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPointTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPointTests.java index 5063901dfb..c066b1eb97 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPointTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPointTests.java @@ -77,6 +77,19 @@ public class BearerTokenAuthenticationEntryPointTests { } + @Test + public void commenceWhenNoBearerTokenErrorAndResourceMetadataResolverSetThenStatus401AndAuthHeaderWithResolvedResourceMetadata() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setAttribute("resource_id", "https://example.com/resource-from-request"); + MockHttpServletResponse response = new MockHttpServletResponse(); + this.authenticationEntryPoint + .setResourceMetadataParameterResolver((req) -> req.getAttribute("resource_id").toString()); + this.authenticationEntryPoint.commence(request, response, new BadCredentialsException("test")); + assertThat(response.getStatus()).isEqualTo(401); + assertThat(response.getHeader("WWW-Authenticate")) + .isEqualTo("Bearer resource_metadata=\"https://example.com/resource-from-request\""); + } + @Test public void commenceWhenInvalidRequestErrorThenStatus400AndHeaderWithError() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest();