Răsfoiți Sursa

Add authorization server metadata for DPoP support

Issue gh-1813

Closes gh-1951
Joe Grandja 5 luni în urmă
părinte
comite
65e3a5ec9b
9 a modificat fișierele cu 160 adăugiri și 10 ștergeri
  1. 46 1
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java
  2. 16 1
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java
  3. 13 1
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java
  4. 3 1
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java
  5. 17 1
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
  6. 18 2
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
  7. 41 1
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java
  8. 3 1
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java
  9. 3 1
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java

+ 46 - 1
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 import org.springframework.security.oauth2.server.authorization.util.SpringAuthorizationServerVersion;
 import org.springframework.util.Assert;
 
@@ -48,6 +49,9 @@ import org.springframework.util.Assert;
  * @see <a target="_blank" href=
  * "https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client
  * Certificate-Bound Access Tokens Metadata</a>
+ * @see <a target="_blank" href=
+ * "https://datatracker.ietf.org/doc/html/rfc9449#section-5.1">5.1 OAuth 2.0 Demonstrating
+ * Proof of Possession (DPoP) Metadata</a>
  */
 public abstract class AbstractOAuth2AuthorizationServerMetadata
 		implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
@@ -379,6 +383,37 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata
 					tlsClientCertificateBoundAccessTokens);
 		}
 
+		/**
+		 * Add a {@link JwsAlgorithms JSON Web Signature (JWS) algorithm} to the
+		 * collection of {@code dpop_signing_alg_values_supported} in the resulting
+		 * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
+		 * @param dPoPSigningAlgorithm the {@link JwsAlgorithms JSON Web Signature (JWS)
+		 * algorithm} supported for DPoP Proof JWTs
+		 * @return the {@link AbstractBuilder} for further configuration
+		 * @since 1.5
+		 */
+		public B dPoPSigningAlgorithm(String dPoPSigningAlgorithm) {
+			addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,
+					dPoPSigningAlgorithm);
+			return getThis();
+		}
+
+		/**
+		 * A {@code Consumer} of the {@link JwsAlgorithms JSON Web Signature (JWS)
+		 * algorithms} supported for DPoP Proof JWTs allowing the ability to add, replace,
+		 * or remove.
+		 * @param dPoPSigningAlgorithmsConsumer a {@code Consumer} of the
+		 * {@link JwsAlgorithms JSON Web Signature (JWS) algorithms} supported for DPoP
+		 * Proof JWTs
+		 * @return the {@link AbstractBuilder} for further configuration
+		 * @since 1.5
+		 */
+		public B dPoPSigningAlgorithms(Consumer<List<String>> dPoPSigningAlgorithmsConsumer) {
+			acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,
+					dPoPSigningAlgorithmsConsumer);
+			return getThis();
+		}
+
 		/**
 		 * Use this claim in the resulting
 		 * {@link AbstractOAuth2AuthorizationServerMetadata}.
@@ -506,6 +541,16 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata
 							.get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED),
 						"codeChallengeMethods cannot be empty");
 			}
+			if (getClaims()
+				.get(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED) != null) {
+				Assert.isInstanceOf(List.class,
+						getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED),
+						"dPoPSigningAlgorithms must be of type List");
+				Assert.notEmpty(
+						(List<?>) getClaims()
+							.get(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED),
+						"dPoPSigningAlgorithms cannot be empty");
+			}
 		}
 
 		@SuppressWarnings("unchecked")

+ 16 - 1
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import java.net.URL;
 import java.util.List;
 
 import org.springframework.security.oauth2.core.ClaimAccessor;
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 
 /**
  * A {@link ClaimAccessor} for the "claims" an Authorization Server describes about its
@@ -40,6 +41,9 @@ import org.springframework.security.oauth2.core.ClaimAccessor;
  * @see <a target="_blank" href=
  * "https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client
  * Certificate-Bound Access Tokens Metadata</a>
+ * @see <a target="_blank" href=
+ * "https://datatracker.ietf.org/doc/html/rfc9449#section-5.1">5.1 OAuth 2.0 Demonstrating
+ * Proof of Possession (DPoP) Metadata</a>
  */
 public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {
 
@@ -193,4 +197,15 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
 				OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS));
 	}
 
+	/**
+	 * Returns the {@link JwsAlgorithms JSON Web Signature (JWS) algorithms} supported for
+	 * DPoP Proof JWTs {@code (dpop_signing_alg_values_supported)}.
+	 * @return the {@link JwsAlgorithms JSON Web Signature (JWS) algorithms} supported for
+	 * DPoP Proof JWTs
+	 * @since 1.5
+	 */
+	default List<String> getDPoPSigningAlgorithms() {
+		return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED);
+	}
+
 }

+ 13 - 1
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
  */
 package org.springframework.security.oauth2.server.authorization;
 
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
+
 /**
  * The names of the "claims" an Authorization Server describes about its configuration,
  * used in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0.
@@ -32,6 +34,9 @@ package org.springframework.security.oauth2.server.authorization;
  * @see <a target="_blank" href=
  * "https://datatracker.ietf.org/doc/html/rfc8705#section-3.3">3.3 Mutual-TLS Client
  * Certificate-Bound Access Tokens Metadata</a>
+ * @see <a target="_blank" href=
+ * "https://datatracker.ietf.org/doc/html/rfc9449#section-5.1">5.1 OAuth 2.0 Demonstrating
+ * Proof of Possession (DPoP) Metadata</a>
  */
 public class OAuth2AuthorizationServerMetadataClaimNames {
 
@@ -130,6 +135,13 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
 	 */
 	public static final String TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS = "tls_client_certificate_bound_access_tokens";
 
+	/**
+	 * {@code dpop_signing_alg_values_supported} - the {@link JwsAlgorithms JSON Web
+	 * Signature (JWS) algorithms} supported for DPoP Proof JWTs
+	 * @since 1.5
+	 */
+	public static final String DPOP_SIGNING_ALG_VALUES_SUPPORTED = "dpop_signing_alg_values_supported";
+
 	protected OAuth2AuthorizationServerMetadataClaimNames() {
 	}
 

+ 3 - 1
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -169,6 +169,8 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter
 					collectionStringConverter);
 			claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,
 					collectionStringConverter);
+			claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,
+					collectionStringConverter);
 			this.claimTypeConverter = new ClaimTypeConverter(claimConverters);
 		}
 

+ 17 - 1
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
 import org.springframework.security.oauth2.core.oidc.OidcScopes;
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;
 import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
@@ -118,6 +119,7 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
 			.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
 			.codeChallengeMethod("S256")
 			.tlsClientCertificateBoundAccessTokens(true)
+			.dPoPSigningAlgorithms(dPoPSigningAlgorithms())
 			.subjectType("public")
 			.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())
 			.scope(OidcScopes.OPENID);
@@ -151,6 +153,20 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
 		};
 	}
 
+	private static Consumer<List<String>> dPoPSigningAlgorithms() {
+		return (algs) -> {
+			algs.add(JwsAlgorithms.RS256);
+			algs.add(JwsAlgorithms.RS384);
+			algs.add(JwsAlgorithms.RS512);
+			algs.add(JwsAlgorithms.PS256);
+			algs.add(JwsAlgorithms.PS384);
+			algs.add(JwsAlgorithms.PS512);
+			algs.add(JwsAlgorithms.ES256);
+			algs.add(JwsAlgorithms.ES384);
+			algs.add(JwsAlgorithms.ES512);
+		};
+	}
+
 	private static String asUrl(String issuer, String endpoint) {
 		return UriComponentsBuilder.fromUriString(issuer).path(endpoint).build().toUriString();
 	}

+ 18 - 2
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.springframework.http.server.ServletServerHttpResponse;
 import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;
 import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;
 import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
@@ -115,7 +116,8 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
 			.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
 			.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
 			.codeChallengeMethod("S256")
-			.tlsClientCertificateBoundAccessTokens(true);
+			.tlsClientCertificateBoundAccessTokens(true)
+			.dPoPSigningAlgorithms(dPoPSigningAlgorithms());
 
 		this.authorizationServerMetadataCustomizer.accept(authorizationServerMetadata);
 
@@ -146,6 +148,20 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
 		};
 	}
 
+	private static Consumer<List<String>> dPoPSigningAlgorithms() {
+		return (algs) -> {
+			algs.add(JwsAlgorithms.RS256);
+			algs.add(JwsAlgorithms.RS384);
+			algs.add(JwsAlgorithms.RS512);
+			algs.add(JwsAlgorithms.PS256);
+			algs.add(JwsAlgorithms.PS384);
+			algs.add(JwsAlgorithms.PS512);
+			algs.add(JwsAlgorithms.ES256);
+			algs.add(JwsAlgorithms.ES384);
+			algs.add(JwsAlgorithms.ES512);
+		};
+	}
+
 	private static String asUrl(String issuer, String endpoint) {
 		return UriComponentsBuilder.fromUriString(issuer).path(endpoint).toUriString();
 	}

+ 41 - 1
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import java.util.List;
 import org.junit.jupiter.api.Test;
 
 import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata.Builder;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -63,6 +64,8 @@ public class OAuth2AuthorizationServerMetadataTests {
 			.tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
 			.codeChallengeMethod("S256")
 			.tlsClientCertificateBoundAccessTokens(true)
+			.dPoPSigningAlgorithm(JwsAlgorithms.RS256)
+			.dPoPSigningAlgorithm(JwsAlgorithms.ES256)
 			.claim("a-claim", "a-value")
 			.build();
 
@@ -87,6 +90,8 @@ public class OAuth2AuthorizationServerMetadataTests {
 			.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
 		assertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly("S256");
 		assertThat(authorizationServerMetadata.isTlsClientCertificateBoundAccessTokens()).isTrue();
+		assertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).containsExactly(JwsAlgorithms.RS256,
+				JwsAlgorithms.ES256);
 		assertThat(authorizationServerMetadata.getClaimAsString("a-claim")).isEqualTo("a-value");
 	}
 
@@ -113,6 +118,7 @@ public class OAuth2AuthorizationServerMetadataTests {
 		assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isNull();
 		assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull();
 		assertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull();
+		assertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).isNull();
 	}
 
 	@Test
@@ -152,6 +158,7 @@ public class OAuth2AuthorizationServerMetadataTests {
 			.isEqualTo(url("https://example.com/oauth2/introspect"));
 		assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull();
 		assertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull();
+		assertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).isNull();
 		assertThat(authorizationServerMetadata.getClaimAsString("some-claim")).isEqualTo("some-value");
 	}
 
@@ -191,6 +198,7 @@ public class OAuth2AuthorizationServerMetadataTests {
 			.isEqualTo(url("https://example.com/oauth2/introspect"));
 		assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull();
 		assertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull();
+		assertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).isNull();
 		assertThat(authorizationServerMetadata.getClaimAsString("some-claim")).isEqualTo("some-value");
 	}
 
@@ -536,6 +544,38 @@ public class OAuth2AuthorizationServerMetadataTests {
 		assertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly("some-authentication-method");
 	}
 
+	@Test
+	public void buildWhenDPoPSigningAlgorithmsNotListThenThrowIllegalArgumentException() {
+		Builder builder = this.minimalBuilder.claims((claims) -> claims
+			.put(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED, "not-a-list"));
+
+		assertThatIllegalArgumentException().isThrownBy(builder::build)
+			.withMessageStartingWith("dPoPSigningAlgorithms must be of type List");
+	}
+
+	@Test
+	public void buildWhenDPoPSigningAlgorithmsEmptyListThenThrowIllegalArgumentException() {
+		Builder builder = this.minimalBuilder.claims(
+				(claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,
+						Collections.emptyList()));
+
+		assertThatIllegalArgumentException().isThrownBy(builder::build)
+			.withMessage("dPoPSigningAlgorithms cannot be empty");
+	}
+
+	@Test
+	public void buildWhenDPoPSigningAlgorithmsAddingOrRemovingThenCorrectValues() {
+		OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder
+			.dPoPSigningAlgorithm(JwsAlgorithms.RS256)
+			.dPoPSigningAlgorithms((algs) -> {
+				algs.clear();
+				algs.add(JwsAlgorithms.ES256);
+			})
+			.build();
+
+		assertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).containsExactly(JwsAlgorithms.ES256);
+	}
+
 	@Test
 	public void claimWhenNameNullThenThrowIllegalArgumentException() {
 		assertThatIllegalArgumentException()

+ 3 - 1
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -148,6 +148,8 @@ public class OidcProviderConfigurationEndpointFilterTests {
 				"\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
 		assertThat(providerConfigurationResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]");
 		assertThat(providerConfigurationResponse).contains("\"tls_client_certificate_bound_access_tokens\":true");
+		assertThat(providerConfigurationResponse).contains(
+				"\"dpop_signing_alg_values_supported\":[\"RS256\",\"RS384\",\"RS512\",\"PS256\",\"PS384\",\"PS512\",\"ES256\",\"ES384\",\"ES512\"]");
 		assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]");
 		assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]");
 		assertThat(providerConfigurationResponse).contains("\"userinfo_endpoint\":\"https://example.com/userinfo\"");

+ 3 - 1
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2024 the original author or authors.
+ * Copyright 2020-2025 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -145,6 +145,8 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
 				"\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\",\"tls_client_auth\",\"self_signed_tls_client_auth\"]");
 		assertThat(authorizationServerMetadataResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]");
 		assertThat(authorizationServerMetadataResponse).contains("\"tls_client_certificate_bound_access_tokens\":true");
+		assertThat(authorizationServerMetadataResponse).contains(
+				"\"dpop_signing_alg_values_supported\":[\"RS256\",\"RS384\",\"RS512\",\"PS256\",\"PS384\",\"PS512\",\"ES256\",\"ES384\",\"ES512\"]");
 	}
 
 	@Test