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 76607173ccc..6dc981186e6 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 @@ import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Function; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -51,6 +52,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 +86,7 @@ public void commence(HttpServletRequest request, HttpServletResponse response, 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 +100,16 @@ public void setRealmName(String realmName) { this.realmName = realmName; } + /** + * Set the resolver to compute the {@code resource_metadata} parameter from the + * request. + * @param resourceMetadataParameterResolver + */ + public void setResourceMetadataParameterResolver( + Function resourceMetadataParameterResolver) { + 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 5063901dfb2..c066b1eb97e 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 void commenceWhenNoBearerTokenErrorAndContextPathSetThenStatus401AndAuthH } + @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();