diff --git a/shared/OpenIddict.Extensions/OpenIddictHelpers.cs b/shared/OpenIddict.Extensions/OpenIddictHelpers.cs index 7c9d83ef..382ba763 100644 --- a/shared/OpenIddict.Extensions/OpenIddictHelpers.cs +++ b/shared/OpenIddict.Extensions/OpenIddictHelpers.cs @@ -1094,6 +1094,34 @@ internal static class OpenIddictHelpers return true; } + /// + /// Determines whether the items contained in + /// are of the specified . + /// + /// The . + /// The expected . + /// + /// if the object doesn't contain any value or if all the items + /// are of the specified , otherwise. + /// + public static bool ValidateObjectElements(JsonElement element, JsonValueKind kind) + { + if (element.ValueKind is not JsonValueKind.Object) + { + throw new ArgumentOutOfRangeException(nameof(element)); + } + + foreach (var property in element.EnumerateObject()) + { + if (property.Value.ValueKind != kind) + { + return false; + } + } + + return true; + } + /// /// Note: this implementation was taken from ASP.NET Core. /// diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs index 9fc48bd9..d6c24636 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs @@ -137,6 +137,12 @@ public static partial class OpenIddictClientHandlers element.ValueKind is JsonValueKind.Array && OpenIddictHelpers.ValidateArrayElements(element, JsonValueKind.String), + // The following parameters MUST be formatted as JSON objects and only contain string values: + Metadata.MtlsEndpointAliases + => ((JsonElement) value) is JsonElement element && + element.ValueKind is JsonValueKind.Object && + OpenIddictHelpers.ValidateObjectElements(element, JsonValueKind.String), + // The following parameters MUST be formatted as booleans: Metadata.AuthorizationResponseIssParameterSupported or Metadata.RequirePushedAuthorizationRequests or @@ -513,15 +519,9 @@ public static partial class OpenIddictClientHandlers throw new ArgumentNullException(nameof(context)); } - var aliases = context.Response[Metadata.MtlsEndpointAliases]?.GetNamedParameters(); - if (aliases is not { Count: > 0 }) - { - return default; - } - // Note: as recommended by the specification, values present in the "mtls_endpoint_aliases" node // that can't be recognized as OAuth 2.0 endpoints or are not valid URIs are simply ignored. - var endpoint = (string?) aliases[Metadata.DeviceAuthorizationEndpoint]; + var endpoint = (string?) context.Response[Metadata.MtlsEndpointAliases]?[Metadata.DeviceAuthorizationEndpoint]; if (Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri) && !OpenIddictHelpers.IsImplicitFileUri(uri)) { context.Configuration.MtlsDeviceAuthorizationEndpoint = uri; @@ -555,15 +555,9 @@ public static partial class OpenIddictClientHandlers throw new ArgumentNullException(nameof(context)); } - var aliases = context.Response[Metadata.MtlsEndpointAliases]?.GetNamedParameters(); - if (aliases is not { Count: > 0 }) - { - return default; - } - // Note: as recommended by the specification, values present in the "mtls_endpoint_aliases" node // that can't be recognized as OAuth 2.0 endpoints or are not valid URIs are simply ignored. - var endpoint = (string?) aliases[Metadata.IntrospectionEndpoint]; + var endpoint = (string?) context.Response[Metadata.MtlsEndpointAliases]?[Metadata.IntrospectionEndpoint]; if (Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri) && !OpenIddictHelpers.IsImplicitFileUri(uri)) { context.Configuration.MtlsIntrospectionEndpoint = uri; @@ -596,15 +590,9 @@ public static partial class OpenIddictClientHandlers throw new ArgumentNullException(nameof(context)); } - var aliases = context.Response[Metadata.MtlsEndpointAliases]?.GetNamedParameters(); - if (aliases is not { Count: > 0 }) - { - return default; - } - // Note: as recommended by the specification, values present in the "mtls_endpoint_aliases" node // that can't be recognized as OAuth 2.0 endpoints or are not valid URIs are simply ignored. - var endpoint = (string?) aliases[Metadata.PushedAuthorizationRequestEndpoint]; + var endpoint = (string?) context.Response[Metadata.MtlsEndpointAliases]?[Metadata.PushedAuthorizationRequestEndpoint]; if (Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri) && !OpenIddictHelpers.IsImplicitFileUri(uri)) { context.Configuration.MtlsPushedAuthorizationEndpoint = uri; @@ -637,15 +625,9 @@ public static partial class OpenIddictClientHandlers throw new ArgumentNullException(nameof(context)); } - var aliases = context.Response[Metadata.MtlsEndpointAliases]?.GetNamedParameters(); - if (aliases is not { Count: > 0 }) - { - return default; - } - // Note: as recommended by the specification, values present in the "mtls_endpoint_aliases" node // that can't be recognized as OAuth 2.0 endpoints or are not valid URIs are simply ignored. - var endpoint = (string?) aliases[Metadata.RevocationEndpoint]; + var endpoint = (string?) context.Response[Metadata.MtlsEndpointAliases]?[Metadata.RevocationEndpoint]; if (Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri) && !OpenIddictHelpers.IsImplicitFileUri(uri)) { context.Configuration.MtlsRevocationEndpoint = uri; @@ -678,15 +660,9 @@ public static partial class OpenIddictClientHandlers throw new ArgumentNullException(nameof(context)); } - var aliases = context.Response[Metadata.MtlsEndpointAliases]?.GetNamedParameters(); - if (aliases is not { Count: > 0 }) - { - return default; - } - // Note: as recommended by the specification, values present in the "mtls_endpoint_aliases" node // that can't be recognized as OAuth 2.0 endpoints or are not valid URIs are simply ignored. - var endpoint = (string?) aliases[Metadata.TokenEndpoint]; + var endpoint = (string?) context.Response[Metadata.MtlsEndpointAliases]?[Metadata.TokenEndpoint]; if (Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri) && !OpenIddictHelpers.IsImplicitFileUri(uri)) { context.Configuration.MtlsTokenEndpoint = uri; @@ -719,15 +695,9 @@ public static partial class OpenIddictClientHandlers throw new ArgumentNullException(nameof(context)); } - var aliases = context.Response[Metadata.MtlsEndpointAliases]?.GetNamedParameters(); - if (aliases is not { Count: > 0 }) - { - return default; - } - // Note: as recommended by the specification, values present in the "mtls_endpoint_aliases" node // that can't be recognized as OAuth 2.0 endpoints or are not valid URIs are simply ignored. - var endpoint = (string?) aliases[Metadata.UserInfoEndpoint]; + var endpoint = (string?) context.Response[Metadata.MtlsEndpointAliases]?[Metadata.UserInfoEndpoint]; if (Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri) && !OpenIddictHelpers.IsImplicitFileUri(uri)) { context.Configuration.MtlsUserInfoEndpoint = uri; diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs index 7bedb22f..19684dab 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs @@ -99,6 +99,12 @@ public static partial class OpenIddictValidationHandlers element.ValueKind is JsonValueKind.Array && OpenIddictHelpers.ValidateArrayElements(element, JsonValueKind.String), + // The following parameters MUST be formatted as JSON objects and only contain string values: + Metadata.MtlsEndpointAliases + => ((JsonElement) value) is JsonElement element && + element.ValueKind is JsonValueKind.Object && + OpenIddictHelpers.ValidateObjectElements(element, JsonValueKind.String), + // Parameters that are not in the well-known list can be of any type. _ => true }; @@ -331,15 +337,9 @@ public static partial class OpenIddictValidationHandlers throw new ArgumentNullException(nameof(context)); } - var aliases = context.Response[Metadata.MtlsEndpointAliases]?.GetNamedParameters(); - if (aliases is not { Count: > 0 }) - { - return default; - } - // Note: as recommended by the specification, values present in the "mtls_endpoint_aliases" node // that can't be recognized as OAuth 2.0 endpoints or are not valid URIs are simply ignored. - var endpoint = (string?) aliases[Metadata.IntrospectionEndpoint]; + var endpoint = (string?) context.Response[Metadata.MtlsEndpointAliases]?[Metadata.IntrospectionEndpoint]; if (Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri) && !OpenIddictHelpers.IsImplicitFileUri(uri)) { context.Configuration.MtlsIntrospectionEndpoint = uri;