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)); this.configurers.values().forEach((configurer) -> configurer.configure(httpSecurity));
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils 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)); 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())); .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 @EnableWebSecurity
@Import(OAuth2AuthorizationServerConfiguration.class) @Import(OAuth2AuthorizationServerConfiguration.class)
static class AuthorizationServerConfiguration { 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())); .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) { private ResultMatcher[] defaultConfigurationMatchers(String issuer) {
// @formatter:off // @formatter:off
return new ResultMatcher[] { 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 @EnableWebSecurity
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfigurationWithInvalidIssuerUrl extends AuthorizationServerConfiguration { static class AuthorizationServerConfigurationWithInvalidIssuerUrl extends AuthorizationServerConfiguration {

View File

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

View File

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

View File

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