Return PAR endpoint metadata only when enabled

Issue https://github.com/spring-projects/spring-authorization-server/issues/2219
This commit is contained in:
Joe Grandja 2025-10-20 06:06:05 -04:00
parent 9dc27bee03
commit fc8b6b5863
8 changed files with 110 additions and 8 deletions

View File

@ -482,6 +482,27 @@ public final class OAuth2AuthorizationServerConfigurer
});
}
OAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestEndpointConfigurer = getConfigurer(
OAuth2PushedAuthorizationRequestEndpointConfigurer.class);
if (pushedAuthorizationRequestEndpointConfigurer != null) {
OAuth2AuthorizationServerMetadataEndpointConfigurer authorizationServerMetadataEndpointConfigurer = getConfigurer(
OAuth2AuthorizationServerMetadataEndpointConfigurer.class);
authorizationServerMetadataEndpointConfigurer.addDefaultAuthorizationServerMetadataCustomizer((builder) -> {
AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();
String issuer = authorizationServerContext.getIssuer();
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext
.getAuthorizationServerSettings();
String pushedAuthorizationRequestEndpoint = UriComponentsBuilder.fromUriString(issuer)
.path(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())
.build()
.toUriString();
builder.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint);
});
}
this.configurers.values().forEach((configurer) -> configurer.configure(httpSecurity));
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils

View File

@ -171,6 +171,28 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer {
});
}
OAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestEndpointConfigurer = httpSecurity
.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.getConfigurer(OAuth2PushedAuthorizationRequestEndpointConfigurer.class);
if (pushedAuthorizationRequestEndpointConfigurer != null) {
OidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = getConfigurer(
OidcProviderConfigurationEndpointConfigurer.class);
providerConfigurationEndpointConfigurer.addDefaultProviderConfigurationCustomizer((builder) -> {
AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();
String issuer = authorizationServerContext.getIssuer();
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext
.getAuthorizationServerSettings();
String pushedAuthorizationRequestEndpoint = UriComponentsBuilder.fromUriString(issuer)
.path(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())
.build()
.toUriString();
builder.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint);
});
}
this.configurers.values().forEach((configurer) -> configurer.configure(httpSecurity));
}

View File

@ -185,6 +185,17 @@ public class OAuth2AuthorizationServerMetadataTests {
.andExpect(jsonPath("$.grant_types_supported[4]").value(AuthorizationGrantType.DEVICE_CODE.getValue()));
}
@Test
public void requestWhenAuthorizationServerMetadataRequestAndPushedAuthorizationRequestEnabledThenMetadataResponseIncludesPushedAuthorizationRequestEndpoint()
throws Exception {
this.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$.pushed_authorization_request_endpoint")
.value(ISSUER.concat(this.authorizationServerSettings.getPushedAuthorizationRequestEndpoint())));
}
@EnableWebSecurity
@Import(OAuth2AuthorizationServerConfiguration.class)
static class AuthorizationServerConfiguration {
@ -301,4 +312,26 @@ public class OAuth2AuthorizationServerMetadataTests {
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled
extends AuthorizationServerConfiguration {
// @formatter:off
@Bean
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
http
.oauth2AuthorizationServer((authorizationServer) ->
authorizationServer
.pushedAuthorizationRequestEndpoint(Customizer.withDefaults())
)
.authorizeHttpRequests((authorize) ->
authorize.anyRequest().authenticated()
);
return http.build();
}
// @formatter:on
}
}

View File

@ -159,6 +159,18 @@ public class OidcProviderConfigurationTests {
.andExpect(jsonPath("$.grant_types_supported[4]").value(AuthorizationGrantType.DEVICE_CODE.getValue()));
}
@Test
public void requestWhenConfigurationRequestAndPushedAuthorizationRequestEnabledThenConfigurationResponseIncludesPushedAuthorizationRequestEndpoint()
throws Exception {
this.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled.class).autowire();
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(ISSUER))
.andExpect(jsonPath("$.pushed_authorization_request_endpoint")
.value(ISSUER.concat(this.authorizationServerSettings.getPushedAuthorizationRequestEndpoint())));
}
private ResultMatcher[] defaultConfigurationMatchers(String issuer) {
// @formatter:off
return new ResultMatcher[] {
@ -357,6 +369,26 @@ public class OidcProviderConfigurationTests {
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled
extends AuthorizationServerConfiguration {
// @formatter:off
@Bean
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
http
.oauth2AuthorizationServer((authorizationServer) ->
authorizationServer
.pushedAuthorizationRequestEndpoint(Customizer.withDefaults())
.oidc(Customizer.withDefaults())
);
return http.build();
}
// @formatter:on
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfigurationWithInvalidIssuerUrl extends AuthorizationServerConfiguration {

View File

@ -101,8 +101,6 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
OidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
.pushedAuthorizationRequestEndpoint(
asUrl(issuer, authorizationServerSettings.getPushedAuthorizationRequestEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))

View File

@ -101,8 +101,6 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
.pushedAuthorizationRequestEndpoint(
asUrl(issuer, authorizationServerSettings.getPushedAuthorizationRequestEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))

View File

@ -134,8 +134,7 @@ public class OidcProviderConfigurationEndpointFilterTests {
assertThat(providerConfigurationResponse).contains("\"issuer\":\"https://example.com\"");
assertThat(providerConfigurationResponse)
.contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\"");
assertThat(providerConfigurationResponse)
.contains("\"pushed_authorization_request_endpoint\":\"https://example.com/oauth2/v1/par\"");
assertThat(providerConfigurationResponse).doesNotContain("\"pushed_authorization_request_endpoint\"");
assertThat(providerConfigurationResponse).doesNotContain("\"device_authorization_endpoint\"");
assertThat(providerConfigurationResponse)
.contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\"");

View File

@ -130,8 +130,7 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
assertThat(authorizationServerMetadataResponse).contains("\"issuer\":\"https://example.com\"");
assertThat(authorizationServerMetadataResponse)
.contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\"");
assertThat(authorizationServerMetadataResponse)
.contains("\"pushed_authorization_request_endpoint\":\"https://example.com/oauth2/v1/par\"");
assertThat(authorizationServerMetadataResponse).doesNotContain("\"pushed_authorization_request_endpoint\"");
assertThat(authorizationServerMetadataResponse).doesNotContain("\"device_authorization_endpoint\"");
assertThat(authorizationServerMetadataResponse)
.contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\"");