@@ -893,6 +893,101 @@ public async Task ResourceMetadata_DoesNotAddTrailingSlash()
893893 transport , loggerFactory : LoggerFactory , cancellationToken : TestContext . Current . CancellationToken ) ;
894894 }
895895
896+ [ Fact ]
897+ public void CloneResourceMetadataClonesAllProperties ( )
898+ {
899+ var propertyNames = typeof ( ProtectedResourceMetadata ) . GetProperties ( ) . Select ( property => property . Name ) . ToList ( ) ;
900+
901+ // Set metadata properties to non-default values to verify they're copied.
902+ var metadata = new ProtectedResourceMetadata
903+ {
904+ Resource = "https://example.com/resource" ,
905+ AuthorizationServers = [ "https://auth1.example.com" , "https://auth2.example.com" ] ,
906+ BearerMethodsSupported = [ "header" , "body" , "query" ] ,
907+ ScopesSupported = [ "read" , "write" , "admin" ] ,
908+ JwksUri = "https://example.com/.well-known/jwks.json" ,
909+ ResourceSigningAlgValuesSupported = [ "RS256" , "ES256" ] ,
910+ ResourceName = "Test Resource" ,
911+ ResourceDocumentation = "https://docs.example.com" ,
912+ ResourcePolicyUri = "https://example.com/policy" ,
913+ ResourceTosUri = "https://example.com/terms" ,
914+ TlsClientCertificateBoundAccessTokens = true ,
915+ AuthorizationDetailsTypesSupported = [ "payment_initiation" , "account_information" ] ,
916+ DpopSigningAlgValuesSupported = [ "RS256" , "PS256" ] ,
917+ DpopBoundAccessTokensRequired = true
918+ } ;
919+
920+ var clonedMetadata = metadata . Clone ( ) ;
921+
922+ // Ensure the cloned metadata is not the same instance
923+ Assert . NotSame ( metadata , clonedMetadata ) ;
924+
925+ // Verify Resource property
926+ Assert . Equal ( metadata . Resource , clonedMetadata . Resource ) ;
927+ Assert . True ( propertyNames . Remove ( nameof ( metadata . Resource ) ) ) ;
928+
929+ // Verify AuthorizationServers list is cloned and contains the same values
930+ Assert . NotSame ( metadata . AuthorizationServers , clonedMetadata . AuthorizationServers ) ;
931+ Assert . Equal ( metadata . AuthorizationServers , clonedMetadata . AuthorizationServers ) ;
932+ Assert . True ( propertyNames . Remove ( nameof ( metadata . AuthorizationServers ) ) ) ;
933+
934+ // Verify BearerMethodsSupported list is cloned and contains the same values
935+ Assert . NotSame ( metadata . BearerMethodsSupported , clonedMetadata . BearerMethodsSupported ) ;
936+ Assert . Equal ( metadata . BearerMethodsSupported , clonedMetadata . BearerMethodsSupported ) ;
937+ Assert . True ( propertyNames . Remove ( nameof ( metadata . BearerMethodsSupported ) ) ) ;
938+
939+ // Verify ScopesSupported list is cloned and contains the same values
940+ Assert . NotSame ( metadata . ScopesSupported , clonedMetadata . ScopesSupported ) ;
941+ Assert . Equal ( metadata . ScopesSupported , clonedMetadata . ScopesSupported ) ;
942+ Assert . True ( propertyNames . Remove ( nameof ( metadata . ScopesSupported ) ) ) ;
943+
944+ // Verify JwksUri property
945+ Assert . Equal ( metadata . JwksUri , clonedMetadata . JwksUri ) ;
946+ Assert . True ( propertyNames . Remove ( nameof ( metadata . JwksUri ) ) ) ;
947+
948+ // Verify ResourceSigningAlgValuesSupported list is cloned (nullable list)
949+ Assert . NotSame ( metadata . ResourceSigningAlgValuesSupported , clonedMetadata . ResourceSigningAlgValuesSupported ) ;
950+ Assert . Equal ( metadata . ResourceSigningAlgValuesSupported , clonedMetadata . ResourceSigningAlgValuesSupported ) ;
951+ Assert . True ( propertyNames . Remove ( nameof ( metadata . ResourceSigningAlgValuesSupported ) ) ) ;
952+
953+ // Verify ResourceName property
954+ Assert . Equal ( metadata . ResourceName , clonedMetadata . ResourceName ) ;
955+ Assert . True ( propertyNames . Remove ( nameof ( metadata . ResourceName ) ) ) ;
956+
957+ // Verify ResourceDocumentation property
958+ Assert . Equal ( metadata . ResourceDocumentation , clonedMetadata . ResourceDocumentation ) ;
959+ Assert . True ( propertyNames . Remove ( nameof ( metadata . ResourceDocumentation ) ) ) ;
960+
961+ // Verify ResourcePolicyUri property
962+ Assert . Equal ( metadata . ResourcePolicyUri , clonedMetadata . ResourcePolicyUri ) ;
963+ Assert . True ( propertyNames . Remove ( nameof ( metadata . ResourcePolicyUri ) ) ) ;
964+
965+ // Verify ResourceTosUri property
966+ Assert . Equal ( metadata . ResourceTosUri , clonedMetadata . ResourceTosUri ) ;
967+ Assert . True ( propertyNames . Remove ( nameof ( metadata . ResourceTosUri ) ) ) ;
968+
969+ // Verify TlsClientCertificateBoundAccessTokens property
970+ Assert . Equal ( metadata . TlsClientCertificateBoundAccessTokens , clonedMetadata . TlsClientCertificateBoundAccessTokens ) ;
971+ Assert . True ( propertyNames . Remove ( nameof ( metadata . TlsClientCertificateBoundAccessTokens ) ) ) ;
972+
973+ // Verify AuthorizationDetailsTypesSupported list is cloned (nullable list)
974+ Assert . NotSame ( metadata . AuthorizationDetailsTypesSupported , clonedMetadata . AuthorizationDetailsTypesSupported ) ;
975+ Assert . Equal ( metadata . AuthorizationDetailsTypesSupported , clonedMetadata . AuthorizationDetailsTypesSupported ) ;
976+ Assert . True ( propertyNames . Remove ( nameof ( metadata . AuthorizationDetailsTypesSupported ) ) ) ;
977+
978+ // Verify DpopSigningAlgValuesSupported list is cloned (nullable list)
979+ Assert . NotSame ( metadata . DpopSigningAlgValuesSupported , clonedMetadata . DpopSigningAlgValuesSupported ) ;
980+ Assert . Equal ( metadata . DpopSigningAlgValuesSupported , clonedMetadata . DpopSigningAlgValuesSupported ) ;
981+ Assert . True ( propertyNames . Remove ( nameof ( metadata . DpopSigningAlgValuesSupported ) ) ) ;
982+
983+ // Verify DpopBoundAccessTokensRequired property
984+ Assert . Equal ( metadata . DpopBoundAccessTokensRequired , clonedMetadata . DpopBoundAccessTokensRequired ) ;
985+ Assert . True ( propertyNames . Remove ( nameof ( metadata . DpopBoundAccessTokensRequired ) ) ) ;
986+
987+ // Ensure we've checked every property. When new properties get added, we'll have to update this test along with the Clone implementation.
988+ Assert . Empty ( propertyNames ) ;
989+ }
990+
896991 [ Fact ]
897992 public async Task ResourceMetadata_PreservesExplicitTrailingSlash ( )
898993 {
0 commit comments