diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java
index e1254f0c..04fe1e5a 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java
@@ -52,6 +52,9 @@ import org.springframework.util.Assert;
* @see 5.1 OAuth 2.0 Demonstrating
* Proof of Possession (DPoP) Metadata
+ * @see 5.
+ * OAuth 2.0 Pushed Authorization Requests Metadata
*/
public abstract class AbstractOAuth2AuthorizationServerMetadata
implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
@@ -119,6 +122,19 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata
return claim(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, authorizationEndpoint);
}
+ /**
+ * Use this {@code pushed_authorization_request_endpoint} in the resulting
+ * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
+ * @param pushedAuthorizationRequestEndpoint the {@code URL} of the OAuth 2.0
+ * Pushed Authorization Request Endpoint
+ * @return the {@link AbstractBuilder} for further configuration
+ * @since 1.5
+ */
+ public B pushedAuthorizationRequestEndpoint(String pushedAuthorizationRequestEndpoint) {
+ return claim(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,
+ pushedAuthorizationRequestEndpoint);
+ }
+
/**
* Use this {@code device_authorization_endpoint} in the resulting
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
@@ -454,6 +470,13 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata
"authorizationEndpoint cannot be null");
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT),
"authorizationEndpoint must be a valid URL");
+ if (getClaims()
+ .get(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT) != null) {
+ validateURL(
+ getClaims()
+ .get(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT),
+ "pushedAuthorizationRequestEndpoint must be a valid URL");
+ }
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT) != null) {
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT),
"deviceAuthorizationEndpoint must be a valid URL");
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java
index a7ceb35e..ba88f8b5 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java
@@ -44,6 +44,9 @@ import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
* @see 5.1 OAuth 2.0 Demonstrating
* Proof of Possession (DPoP) Metadata
+ * @see 5.
+ * OAuth 2.0 Pushed Authorization Requests Metadata
*/
public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {
@@ -65,6 +68,16 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT);
}
+ /**
+ * Returns the {@code URL} of the OAuth 2.0 Pushed Authorization Request Endpoint
+ * {@code (pushed_authorization_request_endpoint)}.
+ * @return the {@code URL} of the OAuth 2.0 Pushed Authorization Request Endpoint
+ * @since 1.5
+ */
+ default URL getPushedAuthorizationRequestEndpoint() {
+ return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT);
+ }
+
/**
* Returns the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
* {@code (device_authorization_endpoint)}.
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java
index 7a47498b..c8ee980e 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java
@@ -37,6 +37,9 @@ import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
* @see 5.1 OAuth 2.0 Demonstrating
* Proof of Possession (DPoP) Metadata
+ * @see 5.
+ * OAuth 2.0 Pushed Authorization Requests Metadata
*/
public class OAuth2AuthorizationServerMetadataClaimNames {
@@ -52,6 +55,13 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
*/
public static final String AUTHORIZATION_ENDPOINT = "authorization_endpoint";
+ /**
+ * {@code pushed_authorization_request_endpoint} - the {@code URL} of the OAuth 2.0
+ * Pushed Authorization Request Endpoint
+ * @since 1.5
+ */
+ public static final String PUSHED_AUTHORIZATION_REQUEST_ENDPOINT = "pushed_authorization_request_endpoint";
+
/**
* {@code device_authorization_endpoint} - the {@code URL} of the OAuth 2.0 Device
* Authorization Endpoint
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java
index 3339ebea..f904987a 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java
@@ -148,6 +148,8 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter
Map> claimConverters = new HashMap<>();
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, urlConverter);
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter);
+ claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,
+ urlConverter);
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT,
urlConverter);
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, urlConverter);
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
index b527396f..43dffe51 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
@@ -101,6 +101,8 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
OidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
+ .pushedAuthorizationRequestEndpoint(
+ asUrl(issuer, authorizationServerSettings.getPushedAuthorizationRequestEndpoint()))
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
index 608766ae..c2f9e067 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
@@ -101,6 +101,8 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
+ .pushedAuthorizationRequestEndpoint(
+ asUrl(issuer, authorizationServerSettings.getPushedAuthorizationRequestEndpoint()))
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java
index 86fa599a..b9afdeed 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java
@@ -51,6 +51,7 @@ public class OAuth2AuthorizationServerMetadataTests {
OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()
.issuer("https://example.com")
.authorizationEndpoint("https://example.com/oauth2/authorize")
+ .pushedAuthorizationRequestEndpoint("https://example.com/oauth2/par")
.tokenEndpoint("https://example.com/oauth2/token")
.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
.jwkSetUrl("https://example.com/oauth2/jwks")
@@ -72,6 +73,8 @@ public class OAuth2AuthorizationServerMetadataTests {
assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com"));
assertThat(authorizationServerMetadata.getAuthorizationEndpoint())
.isEqualTo(url("https://example.com/oauth2/authorize"));
+ assertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint())
+ .isEqualTo(url("https://example.com/oauth2/par"));
assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token"));
assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods())
.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
@@ -107,6 +110,7 @@ public class OAuth2AuthorizationServerMetadataTests {
assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com"));
assertThat(authorizationServerMetadata.getAuthorizationEndpoint())
.isEqualTo(url("https://example.com/oauth2/authorize"));
+ assertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint()).isNull();
assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token"));
assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull();
assertThat(authorizationServerMetadata.getJwkSetUrl()).isNull();
@@ -127,6 +131,8 @@ public class OAuth2AuthorizationServerMetadataTests {
claims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, "https://example.com");
claims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT,
"https://example.com/oauth2/authorize");
+ claims.put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,
+ "https://example.com/oauth2/par");
claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, "https://example.com/oauth2/token");
claims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, "https://example.com/oauth2/jwks");
claims.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, Collections.singletonList("openid"));
@@ -145,6 +151,8 @@ public class OAuth2AuthorizationServerMetadataTests {
assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com"));
assertThat(authorizationServerMetadata.getAuthorizationEndpoint())
.isEqualTo(url("https://example.com/oauth2/authorize"));
+ assertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint())
+ .isEqualTo(url("https://example.com/oauth2/par"));
assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token"));
assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull();
assertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks"));
@@ -168,6 +176,8 @@ public class OAuth2AuthorizationServerMetadataTests {
claims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, url("https://example.com"));
claims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT,
url("https://example.com/oauth2/authorize"));
+ claims.put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,
+ url("https://example.com/oauth2/par"));
claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, url("https://example.com/oauth2/token"));
claims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, url("https://example.com/oauth2/jwks"));
claims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED,
@@ -185,6 +195,8 @@ public class OAuth2AuthorizationServerMetadataTests {
assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com"));
assertThat(authorizationServerMetadata.getAuthorizationEndpoint())
.isEqualTo(url("https://example.com/oauth2/authorize"));
+ assertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint())
+ .isEqualTo(url("https://example.com/oauth2/par"));
assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token"));
assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull();
assertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks"));
@@ -264,6 +276,15 @@ public class OAuth2AuthorizationServerMetadataTests {
.withMessage("authorizationEndpoint must be a valid URL");
}
+ @Test
+ public void buildWhenPushedAuthorizationRequestEndpointNotUrlThenThrowIllegalArgumentException() {
+ Builder builder = this.minimalBuilder.claims((claims) -> claims
+ .put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT, "not an url"));
+
+ assertThatIllegalArgumentException().isThrownBy(builder::build)
+ .withMessage("pushedAuthorizationRequestEndpoint must be a valid URL");
+ }
+
@Test
public void buildWhenMissingTokenEndpointThenThrowsIllegalArgumentException() {
Builder builder = this.minimalBuilder
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java
index 80b70e30..467682d4 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java
@@ -96,6 +96,7 @@ public class OidcProviderConfigurationEndpointFilterTests {
public void doFilterWhenConfigurationRequestThenConfigurationResponse() throws Exception {
String issuer = "https://example.com";
String authorizationEndpoint = "/oauth2/v1/authorize";
+ String pushedAuthorizationRequestEndpoint = "/oauth2/v1/par";
String tokenEndpoint = "/oauth2/v1/token";
String jwkSetEndpoint = "/oauth2/v1/jwks";
String userInfoEndpoint = "/userinfo";
@@ -106,6 +107,7 @@ public class OidcProviderConfigurationEndpointFilterTests {
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()
.issuer(issuer)
.authorizationEndpoint(authorizationEndpoint)
+ .pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint)
.tokenEndpoint(tokenEndpoint)
.jwkSetEndpoint(jwkSetEndpoint)
.oidcUserInfoEndpoint(userInfoEndpoint)
@@ -131,6 +133,8 @@ 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)
.contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\"");
assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/v1/jwks\"");
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java
index 4f9de8e1..34ee4307 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java
@@ -96,6 +96,7 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
public void doFilterWhenAuthorizationServerMetadataRequestThenMetadataResponse() throws Exception {
String issuer = "https://example.com";
String authorizationEndpoint = "/oauth2/v1/authorize";
+ String pushedAuthorizationRequestEndpoint = "/oauth2/v1/par";
String tokenEndpoint = "/oauth2/v1/token";
String jwkSetEndpoint = "/oauth2/v1/jwks";
String tokenRevocationEndpoint = "/oauth2/v1/revoke";
@@ -104,6 +105,7 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()
.issuer(issuer)
.authorizationEndpoint(authorizationEndpoint)
+ .pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint)
.tokenEndpoint(tokenEndpoint)
.jwkSetEndpoint(jwkSetEndpoint)
.tokenRevocationEndpoint(tokenRevocationEndpoint)
@@ -127,6 +129,8 @@ 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)
.contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\"");
assertThat(authorizationServerMetadataResponse).contains(