diff --git a/Directory.Build.targets b/Directory.Build.targets index fe5cadfe..ba23bad2 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -100,6 +100,8 @@ $(DefineConstants);SUPPORTS_CERTIFICATE_LOADER + $(DefineConstants);SUPPORTS_JSON_ELEMENT_DEEP_EQUALS + $(DefineConstants);SUPPORTS_JSON_ELEMENT_PROPERTY_COUNT /// The identity. /// The destinations, returned as a flattened dictionary. - public static ImmutableDictionary GetDestinations(this ClaimsIdentity identity) + public static ImmutableDictionary> GetDestinations(this ClaimsIdentity identity) { if (identity is null) { throw new ArgumentNullException(nameof(identity)); } - var builder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + var builder = ImmutableDictionary.CreateBuilder>(StringComparer.Ordinal); foreach (var group in identity.Claims.GroupBy(claim => claim.Type)) { @@ -676,14 +676,14 @@ public static class OpenIddictExtensions /// /// The principal. /// The destinations, returned as a flattened dictionary. - public static ImmutableDictionary GetDestinations(this ClaimsPrincipal principal) + public static ImmutableDictionary> GetDestinations(this ClaimsPrincipal principal) { if (principal is null) { throw new ArgumentNullException(nameof(principal)); } - var builder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + var builder = ImmutableDictionary.CreateBuilder>(StringComparer.Ordinal); foreach (var group in principal.Claims.GroupBy(claim => claim.Type)) { @@ -714,7 +714,8 @@ public static class OpenIddictExtensions /// The identity. /// The destinations, as a flattened dictionary. /// The identity. - public static ClaimsIdentity SetDestinations(this ClaimsIdentity identity, ImmutableDictionary destinations) + public static ClaimsIdentity SetDestinations(this ClaimsIdentity identity, + ImmutableDictionary> destinations) { if (identity is null) { @@ -743,7 +744,8 @@ public static class OpenIddictExtensions /// The principal. /// The destinations, as a flattened dictionary. /// The principal. - public static ClaimsPrincipal SetDestinations(this ClaimsPrincipal principal, ImmutableDictionary destinations) + public static ClaimsPrincipal SetDestinations(this ClaimsPrincipal principal, + ImmutableDictionary> destinations) { if (principal is null) { diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs index f0fa2dfc..4dff1439 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs @@ -4,9 +4,11 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; @@ -141,17 +143,15 @@ public class OpenIddictMessage continue; } - var values = parameter.Select(parameter => parameter.Value).ToArray(); - // Note: the core OAuth 2.0 specification requires that request parameters // not be present more than once but derived specifications like the // token exchange specification deliberately allow specifying multiple // parameters with the same name to represent a multi-valued parameter. - AddParameter(parameter.Key, values.Length switch + AddParameter(parameter.Key, parameter.Select(parameter => parameter.Value).ToArray() switch { - 0 => default, - 1 => values[0], - _ => values + [] => default, + [string value] => new OpenIddictParameter(value), + [..] values => new(ImmutableCollectionsMarshal.AsImmutableArray(values)) }); } } @@ -161,7 +161,7 @@ public class OpenIddictMessage /// /// The message parameters. /// Parameters with a null or empty key are always ignored. - public OpenIddictMessage(IEnumerable> parameters) + public OpenIddictMessage(IEnumerable?>> parameters) { if (parameters is null) { @@ -180,11 +180,11 @@ public class OpenIddictMessage // not be present more than once but derived specifications like the // token exchange specification deliberately allow specifying multiple // parameters with the same name to represent a multi-valued parameter. - AddParameter(parameter.Key, parameter.Value?.Length switch + AddParameter(parameter.Key, parameter.Value switch { - null or 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value + null or [] => default, + [string value] => new OpenIddictParameter(value), + [..] values => new OpenIddictParameter(values) }); } } @@ -213,11 +213,11 @@ public class OpenIddictMessage // not be present more than once but derived specifications like the // token exchange specification deliberately allow specifying multiple // parameters with the same name to represent a multi-valued parameter. - AddParameter(parameter.Key, parameter.Value.Count switch + AddParameter(parameter.Key, parameter.Value switch { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() + [] => default, + [string value] => new OpenIddictParameter(value), + [..] values => new(ImmutableCollectionsMarshal.AsImmutableArray(values.ToArray())) }); } } @@ -243,17 +243,15 @@ public class OpenIddictMessage continue; } - var values = parameters.GetValues(name); - // Note: the core OAuth 2.0 specification requires that request parameters // not be present more than once but derived specifications like the // token exchange specification deliberately allow specifying multiple // parameters with the same name to represent a multi-valued parameter. - AddParameter(name, values?.Length switch + AddParameter(name, parameters.GetValues(name) switch { - null or 0 => default, - 1 => values[0], - _ => values + null or [] => default, + [string value] => new OpenIddictParameter(value), + [..] values => new(ImmutableCollectionsMarshal.AsImmutableArray(values)) }); } } diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs index 5c27e9d3..7825b108 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs @@ -8,64 +8,88 @@ using System.Collections.Immutable; using System.ComponentModel; using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.Json; using System.Text.Json.Nodes; +using Microsoft.Extensions.Primitives; namespace OpenIddict.Abstractions; /// -/// Represents an OpenIddict parameter value, that can be either a primitive value, -/// an array of strings or a complex JSON representation containing child nodes. +/// Represents an OpenIddict parameter value and provides two-way conversion operators that allow +/// representing it as a primitive value, an immutable array of strings or a JSON element or node. /// public readonly struct OpenIddictParameter : IEquatable { + private readonly object? _value; + /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(bool value) => Value = value; + public OpenIddictParameter(bool value) => _value = value; /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(bool? value) => Value = value; + public OpenIddictParameter(bool? value) => _value = value; /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(JsonElement value) => Value = value; + public OpenIddictParameter(JsonElement value) => _value = value; /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(JsonNode? value) => Value = value; + public OpenIddictParameter(JsonNode? value) => _value = value switch + { + // Clone the node to ensure the stored value cannot be mutated. + JsonNode node => node.DeepClone(), + + null => null + }; /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(long value) => Value = value; + public OpenIddictParameter(long value) => _value = value; /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(long? value) => Value = value; + public OpenIddictParameter(long? value) => _value = value; /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(string? value) => Value = value; + public OpenIddictParameter(string? value) => _value = value; /// /// Initializes a new parameter using the specified value. /// /// The parameter value. - public OpenIddictParameter(string?[]? value) => Value = value; + public OpenIddictParameter(ImmutableArray value) + // Note: to avoid boxing, the underlying array is directly stored as the backing value. + => _value = ImmutableCollectionsMarshal.AsArray(value); + + /// + /// Initializes a new parameter using the specified value. + /// + /// The parameter value. + public OpenIddictParameter(ImmutableArray? value) => _value = value switch + { + // Note: to avoid boxing, the underlying array is directly stored as the backing value. + ImmutableArray array => ImmutableCollectionsMarshal.AsArray(array), + + null => null + }; /// /// Gets the child item corresponding to the specified index. @@ -89,9 +113,9 @@ public readonly struct OpenIddictParameter : IEquatable { get { - return Value switch + return _value switch { - // If the parameter is a primitive array of strings, return its length. + // If the parameter is an array of strings, return its length. string?[] value => value.Length, // If the parameter is a JSON array or a JSON object, return its length. @@ -135,6 +159,9 @@ public readonly struct OpenIddictParameter : IEquatable return element.GetArrayLength(); case JsonValueKind.Object: +#if SUPPORTS_JSON_ELEMENT_PROPERTY_COUNT + return element.GetPropertyCount(); +#else var count = 0; using (var enumerator = element.EnumerateObject()) @@ -149,6 +176,7 @@ public readonly struct OpenIddictParameter : IEquatable } return count; +#endif default: return 0; } @@ -157,11 +185,20 @@ public readonly struct OpenIddictParameter : IEquatable } /// - /// Gets the associated value, that can be either a primitive CLR type - /// (e.g bool, string, long), an array of strings or a complex JSON object. + /// Gets the associated raw value, that can be either a primitive CLR type + /// (e.g bool, string, long), an immutable array of strings or a complex JSON object. /// - [EditorBrowsable(EditorBrowsableState.Advanced)] - public object? Value { get; } + [EditorBrowsable(EditorBrowsableState.Never)] + public object? GetRawValue() => _value switch + { + // If the value is backed by an array of strings or a JSON node, return a copy instead of the + // real instance to ensure mutations made on the returned object don't affect the stored array. + string?[] array => ImmutableArray.Create(array), + JsonNode node => node.DeepClone(), + + object value => value, + null => null + }; /// /// Determines whether the current @@ -174,7 +211,7 @@ public readonly struct OpenIddictParameter : IEquatable /// public bool Equals(OpenIddictParameter other) { - return (left: Value, right: other.Value) switch + return (_value, other._value) switch { // If the two parameters reference the same instance, return true. // @@ -207,6 +244,9 @@ public readonly struct OpenIddictParameter : IEquatable // If the two parameters are JsonElement instances, use the custom comparer. (JsonElement left, JsonElement right) => DeepEquals(left, right), + // If the two parameters are JsonNode instances, use JsonNode.DeepEquals(). + (JsonNode left, JsonNode right) => JsonNode.DeepEquals(left, right), + // When one of the parameters is a JsonElement, compare their underlying values. (JsonElement { ValueKind: JsonValueKind.True }, bool right) => right, (bool left, JsonElement { ValueKind: JsonValueKind.True }) => left, @@ -252,8 +292,9 @@ public readonly struct OpenIddictParameter : IEquatable static bool DeepEquals(JsonElement left, JsonElement right) { +#if !SUPPORTS_JSON_ELEMENT_DEEP_EQUALS RuntimeHelpers.EnsureSufficientExecutionStack(); - +#endif switch ((left.ValueKind, right.ValueKind)) { case (JsonValueKind.Undefined, JsonValueKind.Undefined): @@ -267,6 +308,9 @@ public readonly struct OpenIddictParameter : IEquatable case (JsonValueKind.Null, JsonValueKind.Undefined): return true; +#if SUPPORTS_JSON_ELEMENT_DEEP_EQUALS + default: return JsonElement.DeepEquals(left, right); +#else case (JsonValueKind.Number, JsonValueKind.Number): return string.Equals(left.GetRawText(), right.GetRawText(), StringComparison.Ordinal); @@ -312,6 +356,7 @@ public readonly struct OpenIddictParameter : IEquatable } default: return false; +#endif } } } @@ -333,7 +378,7 @@ public readonly struct OpenIddictParameter : IEquatable /// The hash code for the current instance. public override int GetHashCode() { - return Value switch + return _value switch { // When the parameter value is null, return 0. null => 0, @@ -460,7 +505,7 @@ public readonly struct OpenIddictParameter : IEquatable /// A dictionary of all the parameters associated with the current instance. public IReadOnlyDictionary GetNamedParameters() { - return Value switch + return _value switch { // When the parameter is a JsonElement representing an object, return the requested item. JsonElement { ValueKind: JsonValueKind.Object } value => GetParametersFromJsonElement(value), @@ -510,7 +555,7 @@ public readonly struct OpenIddictParameter : IEquatable /// An enumeration of all the unnamed parameters associated with the current instance. public IReadOnlyList GetUnnamedParameters() { - return Value switch + return _value switch { // When the parameter is an array of strings, return its items. string?[] value => GetParametersFromArray(value), @@ -573,7 +618,7 @@ public readonly struct OpenIddictParameter : IEquatable /// Returns the representation of the current instance. /// /// The representation associated with the parameter value. - public override string? ToString() => Value switch + public override string? ToString() => _value switch { null => string.Empty, @@ -633,7 +678,7 @@ public readonly struct OpenIddictParameter : IEquatable throw new ArgumentException(SR.GetResourceString(SR.ID0192), nameof(name)); } - var result = Value switch + var result = _value switch { // When the parameter is a JsonElement representing an array, return the requested item. JsonElement { ValueKind: JsonValueKind.Object } element => @@ -669,7 +714,7 @@ public readonly struct OpenIddictParameter : IEquatable throw new ArgumentOutOfRangeException(nameof(index), SR.GetResourceString(SR.ID0193)); } - var result = Value switch + var result = _value switch { // When the parameter is an array of strings, return the requested item. string?[] array => index < array.Length ? new(array[index]) : null, @@ -706,7 +751,7 @@ public readonly struct OpenIddictParameter : IEquatable throw new ArgumentNullException(nameof(writer)); } - switch (Value) + switch (_value) { // Note: undefined JsonElement values are assimilated to null values. case null: @@ -778,7 +823,7 @@ public readonly struct OpenIddictParameter : IEquatable /// The converted value. public static explicit operator bool?(OpenIddictParameter? parameter) { - return parameter?.Value switch + return parameter?._value switch { // When the parameter is a null value or a JsonElement representing null, return null. null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, @@ -833,7 +878,7 @@ public readonly struct OpenIddictParameter : IEquatable /// The converted value. public static explicit operator JsonElement(OpenIddictParameter? parameter) { - return parameter?.Value switch + return parameter?._value switch { // When the parameter is a null value, return an undefined JsonElement. null => default, @@ -876,13 +921,14 @@ public readonly struct OpenIddictParameter : IEquatable /// The converted value. public static explicit operator JsonNode?(OpenIddictParameter? parameter) { - return parameter?.Value switch + return parameter?._value switch { // When the parameter is a null value or a JsonElement representing null, return null. null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, - // When the parameter is already a JsonNode, return it as-is. - JsonNode value => value, + // When the parameter is already a JsonNode, return a clone to ensure mutations + // made on the returned object do not affect the instance stored by this structure. + JsonNode value => value.DeepClone(), // When the parameter is a boolean, return a JsonValue. bool value => JsonValue.Create(value), @@ -972,7 +1018,7 @@ public readonly struct OpenIddictParameter : IEquatable /// The converted value. public static explicit operator long?(OpenIddictParameter? parameter) { - return parameter?.Value switch + return parameter?._value switch { // When the parameter is a null value or a JsonElement representing null, return null. null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, @@ -1030,7 +1076,7 @@ public readonly struct OpenIddictParameter : IEquatable /// The converted value. public static explicit operator string?(OpenIddictParameter? parameter) { - return parameter?.Value switch + return parameter?._value switch { // When the parameter is a null value or a JsonElement representing null, return null. null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, @@ -1091,19 +1137,145 @@ public readonly struct OpenIddictParameter : IEquatable } /// - /// Converts an instance to an array of strings. + /// Converts an instance to a instance. /// /// The parameter to convert. /// The converted value. - public static explicit operator string?[]?(OpenIddictParameter? parameter) + public static explicit operator StringValues(OpenIddictParameter? parameter) + => ((StringValues?) parameter).GetValueOrDefault(); + + /// + /// Converts an instance to a instance. + /// + /// The parameter to convert. + /// The converted value. + public static explicit operator StringValues?(OpenIddictParameter? parameter) { - return parameter?.Value switch + return parameter?._value switch + { + // When the parameter is a null value or a JsonElement representing null, return null. + null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, + + // When the parameter is an array of strings, return a StringValues instance wrapping the cloned array. + string?[] value => new StringValues(value.ToArray().ToArray()), + + // When the parameter is a string value, return a StringValues instance with a single entry. + string value => new StringValues(value), + + // When the parameter is a boolean value, return a StringValues instance with its string representation. + bool value => new StringValues(value ? "true" : "false"), + + // When the parameter is an integer, return a StringValues instance with its string representation. + long value => new StringValues(value.ToString(CultureInfo.InvariantCulture)), + + // When the parameter is a JSON boolean value, return a StringValues instance with its string representation. + JsonElement { ValueKind: JsonValueKind.True } => new StringValues("true"), + JsonElement { ValueKind: JsonValueKind.False } => new StringValues("false"), + + // When the parameter is a JsonElement, try to convert it if it's of a supported type. + JsonElement value => ConvertFromJsonElement(value), + + // When the parameter is a JsonValue wrapping a JsonElement, + // apply the same logic as with direct JsonElement instances. + JsonValue value when value.TryGetValue(out JsonElement element) => ConvertFromJsonElement(element), + + // When the parameter is a JsonValue wrapping a string, return a StringValues instance with a single entry. + JsonValue value when value.TryGetValue(out string? result) => new StringValues(result), + + // When the parameter is a JsonValue wrapping a boolean, return a StringValues instance with its string representation. + JsonValue value when value.TryGetValue(out bool result) + => new StringValues(result ? "true" : "false"), + + // When the parameter is a JsonValue wrapping an integer, return a StringValues instance with its string representation. + JsonValue value when value.TryGetValue(out int result) + => new StringValues(result.ToString(CultureInfo.InvariantCulture)), + + JsonValue value when value.TryGetValue(out long result) + => new StringValues(result.ToString(CultureInfo.InvariantCulture)), + + // When the parameter is a JsonNode (e.g a JsonValue wrapping a non-primitive type), + // serialize it to a JsonElement first to determine its actual JSON representation + // and apply the same logic as with non-wrapped JsonElement instances. + JsonNode value when JsonSerializer.SerializeToElement(value) is JsonElement element + => ConvertFromJsonElement(element), + + // If the parameter is of a different type, return null to indicate the conversion failed. + _ => null + }; + + static StringValues? ConvertFromJsonElement(JsonElement element) => element.ValueKind switch + { + // When the parameter is a JsonElement representing a boolean, + // return a StringValues instance with its string representation. + JsonValueKind.True => new StringValues("true"), + JsonValueKind.False => new StringValues("false"), + + // When the parameter is a JsonElement representing a string or a + // number, return a StringValues instance with its string representation. + JsonValueKind.String or JsonValueKind.Number => new StringValues(element.ToString()), + + // When the parameter is a JsonElement representing an array, return the elements as strings. + JsonValueKind.Array => CreateArrayFromJsonElement(element), + + _ => null + }; + + static StringValues? CreateArrayFromJsonElement(JsonElement element) + { + var length = element.GetArrayLength(); + var array = new string[length]; + + for (var index = 0; index < length; index++) + { + var item = element[index]; + if (item.ValueKind is JsonValueKind.True) + { + array[index] = "true"; + } + + else if (item.ValueKind is JsonValueKind.False) + { + array[index] = "false"; + } + + else if (item.ValueKind is JsonValueKind.String or JsonValueKind.Number) + { + array[index] = item.ToString(); + } + + // Always return a null array if one of the items is a not string, a number or a boolean. + else + { + return null; + } + } + + return new StringValues(array); + } + } + + /// + /// Converts an instance to an immutable array of strings. + /// + /// The parameter to convert. + /// The converted value. + public static explicit operator ImmutableArray(OpenIddictParameter? parameter) + => ((ImmutableArray?) parameter).GetValueOrDefault(); + + /// + /// Converts an instance to an immutable array of strings. + /// + /// The parameter to convert. + /// The converted value. + public static explicit operator ImmutableArray?(OpenIddictParameter? parameter) + { + return parameter?._value switch { // When the parameter is a null value or a JsonElement representing null, return null. null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, // When the parameter is already an array of strings, return it as-is. - string?[] value => value, + string?[] value => ImmutableCollectionsMarshal.AsImmutableArray(value), // When the parameter is a string value, return an array with a single entry. string value => [value], @@ -1149,7 +1321,7 @@ public readonly struct OpenIddictParameter : IEquatable _ => null }; - static string?[]? ConvertFromJsonElement(JsonElement element) => element.ValueKind switch + static ImmutableArray? ConvertFromJsonElement(JsonElement element) => element.ValueKind switch { // When the parameter is a JsonElement representing a boolean, // return an 1-item array with its string representation. @@ -1166,27 +1338,27 @@ public readonly struct OpenIddictParameter : IEquatable _ => null }; - static string?[]? CreateArrayFromJsonElement(JsonElement element) + static ImmutableArray? CreateArrayFromJsonElement(JsonElement element) { var length = element.GetArrayLength(); - var array = new string?[length]; + var builder = ImmutableArray.CreateBuilder(length); for (var index = 0; index < length; index++) { var item = element[index]; if (item.ValueKind is JsonValueKind.True) { - array[index] = "true"; + builder.Add("true"); } else if (item.ValueKind is JsonValueKind.False) { - array[index] = "false"; + builder.Add("false"); } else if (item.ValueKind is JsonValueKind.String or JsonValueKind.Number) { - array[index] = item.ToString(); + builder.Add(item.ToString()); } // Always return a null array if one of the items is a not string, a number or a boolean. @@ -1196,65 +1368,96 @@ public readonly struct OpenIddictParameter : IEquatable } } - return array; + return builder.ToImmutable(); } } /// /// Converts a boolean to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. public static implicit operator OpenIddictParameter(bool value) => new(value); /// /// Converts a nullable boolean to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. public static implicit operator OpenIddictParameter(bool? value) => new(value); /// /// Converts a to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. public static implicit operator OpenIddictParameter(JsonElement value) => new(value); /// /// Converts a to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. public static implicit operator OpenIddictParameter(JsonNode? value) => new(value); /// /// Converts a long integer to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. public static implicit operator OpenIddictParameter(long value) => new(value); /// /// Converts a nullable long integer to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. public static implicit operator OpenIddictParameter(long? value) => new(value); /// /// Converts a string to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. public static implicit operator OpenIddictParameter(string? value) => new(value); + /// + /// Converts a string to an instance. + /// + /// The value to convert. + /// An instance. + public static implicit operator OpenIddictParameter(StringValues? value) => value?.Count switch + { + null or 0 => default, + 1 => new OpenIddictParameter(value.GetValueOrDefault()[0]), + _ => new(ImmutableCollectionsMarshal.AsImmutableArray(value.GetValueOrDefault().ToArray())) + }; + + /// + /// Converts a string to an instance. + /// + /// The value to convert. + /// An instance. + public static implicit operator OpenIddictParameter(StringValues value) => value.Count switch + { + 0 => default, + 1 => new OpenIddictParameter(value[0]), + _ => new(ImmutableCollectionsMarshal.AsImmutableArray(value.ToArray())) + }; + + /// + /// Converts an array of strings to an instance. + /// + /// The value to convert. + /// An instance. + public static implicit operator OpenIddictParameter(ImmutableArray value) => new(value); + /// /// Converts an array of strings to an instance. /// - /// The value to convert + /// The value to convert. /// An instance. - public static implicit operator OpenIddictParameter(string?[]? value) => new(value); + public static implicit operator OpenIddictParameter(ImmutableArray? value) => new(value); /// /// Determines whether a parameter is null or empty. @@ -1263,7 +1466,7 @@ public readonly struct OpenIddictParameter : IEquatable /// if the parameter is null or empty, otherwise. public static bool IsNullOrEmpty(OpenIddictParameter parameter) { - return parameter.Value switch + return parameter._value switch { null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => true, @@ -1298,11 +1501,14 @@ public readonly struct OpenIddictParameter : IEquatable return element.GetArrayLength() is 0; case JsonValueKind.Object: +#if SUPPORTS_JSON_ELEMENT_PROPERTY_COUNT + return element.GetPropertyCount() is 0; +#else using (var enumerator = element.EnumerateObject()) { return !enumerator.MoveNext(); } - +#endif default: return false; } } diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs index dec7c9a7..7b72f057 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Collections.Specialized; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -79,7 +80,7 @@ public class OpenIddictRequest : OpenIddictMessage /// /// The request parameters. /// Parameters with a null or empty key are always ignored. - public OpenIddictRequest(IEnumerable> parameters) + public OpenIddictRequest(IEnumerable?>> parameters) : base(parameters) { } @@ -134,18 +135,18 @@ public class OpenIddictRequest : OpenIddictMessage /// /// Gets or sets the "audience" parameters. /// - public string?[]? Audiences + public ImmutableArray? Audiences { - get => (string?[]?) GetParameter(OpenIddictConstants.Parameters.Audience); + get => (ImmutableArray?) GetParameter(OpenIddictConstants.Parameters.Audience); set => SetParameter(OpenIddictConstants.Parameters.Audience, value); } /// /// Gets or sets the "claims" parameter. /// - public JsonElement Claims + public JsonObject? Claims { - get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Claims); + get => (JsonObject?) GetParameter(OpenIddictConstants.Parameters.Claims); set => SetParameter(OpenIddictConstants.Parameters.Claims, value); } @@ -379,9 +380,9 @@ public class OpenIddictRequest : OpenIddictMessage /// /// Gets or sets the "resource" parameters. /// - public string?[]? Resources + public ImmutableArray? Resources { - get => (string?[]?) GetParameter(OpenIddictConstants.Parameters.Resource); + get => (ImmutableArray?) GetParameter(OpenIddictConstants.Parameters.Resource); set => SetParameter(OpenIddictConstants.Parameters.Resource, value); } @@ -442,9 +443,9 @@ public class OpenIddictRequest : OpenIddictMessage /// /// Gets or sets the "registration" parameter. /// - public JsonElement Registration + public JsonObject? Registration { - get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Registration); + get => (JsonObject?) GetParameter(OpenIddictConstants.Parameters.Registration); set => SetParameter(OpenIddictConstants.Parameters.Registration, value); } diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs index 6a27bad4..65225514 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Collections.Specialized; using System.Diagnostics; using System.Text.Json; @@ -78,7 +79,7 @@ public class OpenIddictResponse : OpenIddictMessage /// /// The response parameters. /// Parameters with a null or empty key are always ignored. - public OpenIddictResponse(IEnumerable> parameters) + public OpenIddictResponse(IEnumerable?>> parameters) : base(parameters) { } diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Authentication.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Authentication.cs index 0187420f..ed64fe0a 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Authentication.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Authentication.cs @@ -78,9 +78,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS var location = QueryHelpers.AddQueryString(context.AuthorizationEndpoint, from parameter in context.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select KeyValuePair.Create(parameter.Key, value)); #else @@ -88,9 +88,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers foreach (var (key, value) in from parameter in context.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Session.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Session.cs index 2e974751..7b9c2064 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Session.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Session.cs @@ -78,9 +78,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS var location = QueryHelpers.AddQueryString(context.EndSessionEndpoint, from parameter in context.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select KeyValuePair.Create(parameter.Key, value)); #else @@ -88,9 +88,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers foreach (var (key, value) in from parameter in context.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs index e8c88f23..c3824696 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs @@ -8,6 +8,7 @@ using System.Buffers.Binary; using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Security.Claims; using System.Text; using System.Text.Json; @@ -622,13 +623,17 @@ public static partial class OpenIddictClientAspNetCoreHandlers context.Parameters[parameter.Key] = parameter.Value switch { OpenIddictParameter value => value, - JsonElement value => new OpenIddictParameter(value), - JsonNode value => new OpenIddictParameter(value), - bool value => new OpenIddictParameter(value), - int value => new OpenIddictParameter(value), - long value => new OpenIddictParameter(value), - string value => new OpenIddictParameter(value), - string[] value => new OpenIddictParameter(value), + + JsonElement value => new OpenIddictParameter(value), + JsonNode value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + ImmutableArray value => new OpenIddictParameter(value), + + string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)), + IEnumerable value => new OpenIddictParameter([.. value]), _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) }; @@ -943,13 +948,17 @@ public static partial class OpenIddictClientAspNetCoreHandlers context.Parameters[parameter.Key] = parameter.Value switch { OpenIddictParameter value => value, - JsonElement value => new OpenIddictParameter(value), - JsonNode value => new OpenIddictParameter(value), - bool value => new OpenIddictParameter(value), - int value => new OpenIddictParameter(value), - long value => new OpenIddictParameter(value), - string value => new OpenIddictParameter(value), - string[] value => new OpenIddictParameter(value), + + JsonElement value => new OpenIddictParameter(value), + JsonNode value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + ImmutableArray value => new OpenIddictParameter(value), + + string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)), + IEnumerable value => new OpenIddictParameter([.. value]), _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) }; diff --git a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionFormatter.cs b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionFormatter.cs index c267bbab..8b593bcb 100644 --- a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionFormatter.cs +++ b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionFormatter.cs @@ -52,7 +52,7 @@ public sealed class OpenIddictClientDataProtectionFormatter : IOpenIddictClientD { // Read the version of the format used to serialize the ticket. var version = reader.ReadInt32(); - if (version != 5) + if (version is not 5) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0287)); } diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Authentication.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Authentication.cs index f0fb8953..a369538f 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Authentication.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Authentication.cs @@ -78,9 +78,9 @@ public static partial class OpenIddictClientOwinHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Session.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Session.cs index 4447af9b..873efad4 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Session.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Session.cs @@ -76,9 +76,9 @@ public static partial class OpenIddictClientOwinHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs index 19f327e2..184cf080 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs @@ -14,6 +14,7 @@ using System.Text; using System.Text.Json; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Tokens; using OpenIddict.Extensions; using Owin; @@ -265,7 +266,10 @@ public static partial class OpenIddictClientOwinHandlers if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { - context.Transaction.Request = new OpenIddictRequest(request.Query); + context.Transaction.Request = new OpenIddictRequest( + from parameter in request.Query + let values = new StringValues(parameter.Value) + select new KeyValuePair(parameter.Key, values)); } else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase)) @@ -296,7 +300,10 @@ public static partial class OpenIddictClientOwinHandlers return; } - context.Transaction.Request = new OpenIddictRequest(await request.ReadFormAsync()); + context.Transaction.Request = new OpenIddictRequest( + from parameter in await request.ReadFormAsync() + let values = new StringValues(parameter.Value) + select new KeyValuePair(parameter.Key, values)); } else diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs index f46a9688..2b69c938 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -78,7 +79,9 @@ public sealed class OpenIddictClientSystemIntegrationActivationHandler : IHosted } #endif // Otherwise, try to extract the protocol activation from the command line arguments. - if (GetProtocolActivationUriFromCommandLineArguments(Environment.GetCommandLineArgs()) is Uri value) + var arguments = ImmutableCollectionsMarshal.AsImmutableArray(Environment.GetCommandLineArgs()); + + if (GetProtocolActivationUriFromCommandLineArguments(arguments) is Uri value) { return new OpenIddictClientSystemIntegrationActivation(value); } diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs index a194a34a..1ef54bf5 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs @@ -240,8 +240,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers NSUrl CreateUrl() => new(OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)).AbsoluteUri); void HandleCallback(NSUrl? url, NSError? error) { @@ -253,12 +253,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -270,12 +265,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -370,8 +360,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers intent.LaunchUrl(Application.Context, NativeUri.Parse(OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri)!); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)).AbsoluteUri)!); context.HandleRequest(); #else @@ -455,8 +445,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers requestUri : OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))), + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)), callbackUri: new Uri(context.RedirectUri, UriKind.Absolute))) { case { ResponseStatus: WebAuthenticationStatus.Success } result @@ -467,12 +457,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -484,12 +469,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -576,8 +556,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers var uri = OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs index 052c32d6..05c0d5a1 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs @@ -240,8 +240,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers NSUrl CreateUrl() => new(OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)).AbsoluteUri); void HandleCallback(NSUrl? url, NSError? error) { @@ -253,12 +253,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -270,12 +265,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -370,8 +360,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers intent.LaunchUrl(Application.Context, NativeUri.Parse(OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri)!); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)).AbsoluteUri)!); context.HandleRequest(); #else @@ -455,8 +445,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers requestUri : OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))), + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)), callbackUri: new Uri(context.PostLogoutRedirectUri, UriKind.Absolute))) { case { ResponseStatus: WebAuthenticationStatus.Success } result @@ -467,12 +457,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -484,12 +469,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers { foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -576,8 +556,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers var uri = OpenIddictHelpers.AddQueryStringParameters( uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), parameters: context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs index 242b5f72..5821986b 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; using System.Net; @@ -378,7 +379,7 @@ public static class OpenIddictClientSystemIntegrationHelpers /// The if the application instance was activated /// via a protocol activation, otherwise. /// - internal static Uri? GetProtocolActivationUriFromCommandLineArguments(string?[]? arguments) => arguments switch + internal static Uri? GetProtocolActivationUriFromCommandLineArguments(ImmutableArray arguments) => arguments switch { // In most cases, the first segment present in the command line arguments contains the path of the // executable, but it's technically possible to start an application in a way that the command line diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs index 606c5b4c..1548d3a4 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs @@ -69,12 +69,7 @@ public sealed class OpenIddictClientSystemIntegrationService { foreach (var parameter in OpenIddictHelpers.ParseQuery(intent.Data.Query)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } @@ -86,12 +81,7 @@ public sealed class OpenIddictClientSystemIntegrationService { foreach (var parameter in OpenIddictHelpers.ParseFragment(intent.Data.Fragment)) { - parameters[parameter.Key] = parameter.Value.Count switch - { - 0 => default, - 1 => parameter.Value[0], - _ => parameter.Value.ToArray() - }; + parameters[parameter.Key] = parameter.Value; } } diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs index da2fec76..aa171c40 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs @@ -1044,8 +1044,8 @@ public static partial class OpenIddictClientSystemNetHttpHandlers { request.RequestUri = OpenIddictHelpers.AddQueryStringParameters(request.RequestUri, context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)); } // For POST requests, attach the request parameters to the request form by default. @@ -1053,9 +1053,9 @@ public static partial class OpenIddictClientSystemNetHttpHandlers { request.Content = new FormUrlEncodedContent( from parameter in context.Transaction.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() select new KeyValuePair(parameter.Key, value)); } diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs index a3e61f78..9fc48bd9 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs @@ -1486,24 +1486,12 @@ public static partial class OpenIddictClientHandlers // The following parameters MUST be formatted as arrays of objects: JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element && - element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element), + element.ValueKind is JsonValueKind.Array && + OpenIddictHelpers.ValidateArrayElements(element, JsonValueKind.Object), // Parameters that are not in the well-known list can be of any type. _ => true }; - - static bool ValidateObjectArray(JsonElement element) - { - foreach (var item in element.EnumerateArray()) - { - if (item.ValueKind is not JsonValueKind.Object) - { - return false; - } - } - - return true; - } } } diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs index 18dc8a18..61e4609b 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs @@ -102,9 +102,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS var location = QueryHelpers.AddQueryString(context.RequestUri.GetLeftPart(UriPartial.Path), from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select KeyValuePair.Create(parameter.Key, value)); #else @@ -112,9 +112,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -188,9 +188,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -264,9 +264,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS var location = QueryHelpers.AddQueryString(context.RedirectUri, from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select KeyValuePair.Create(parameter.Key, value)); #else @@ -274,9 +274,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -335,9 +335,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs index ac2b2d93..0575c971 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs @@ -84,9 +84,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS var location = QueryHelpers.AddQueryString(context.RequestUri.GetLeftPart(UriPartial.Path), from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select KeyValuePair.Create(parameter.Key, value)); #else @@ -94,9 +94,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -154,9 +154,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS var location = QueryHelpers.AddQueryString(context.PostLogoutRedirectUri, from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select KeyValuePair.Create(parameter.Key, value)); #else @@ -164,9 +164,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs index e7405851..8b0ed708 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; @@ -257,13 +258,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers context.Parameters[parameter.Key] = parameter.Value switch { OpenIddictParameter value => value, - JsonElement value => new OpenIddictParameter(value), - JsonNode value => new OpenIddictParameter(value), - bool value => new OpenIddictParameter(value), - int value => new OpenIddictParameter(value), - long value => new OpenIddictParameter(value), - string value => new OpenIddictParameter(value), - string[] value => new OpenIddictParameter(value), + + JsonElement value => new OpenIddictParameter(value), + JsonNode value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + ImmutableArray value => new OpenIddictParameter(value), + + string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)), + IEnumerable value => new OpenIddictParameter([.. value]), _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) }; @@ -354,13 +359,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers context.Parameters[parameter.Key] = parameter.Value switch { OpenIddictParameter value => value, - JsonElement value => new OpenIddictParameter(value), - JsonNode value => new OpenIddictParameter(value), - bool value => new OpenIddictParameter(value), - int value => new OpenIddictParameter(value), - long value => new OpenIddictParameter(value), - string value => new OpenIddictParameter(value), - string[] value => new OpenIddictParameter(value), + + JsonElement value => new OpenIddictParameter(value), + JsonNode value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + ImmutableArray value => new OpenIddictParameter(value), + + string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)), + IEnumerable value => new OpenIddictParameter([.. value]), _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) }; @@ -413,13 +422,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers context.Parameters[parameter.Key] = parameter.Value switch { OpenIddictParameter value => value, - JsonElement value => new OpenIddictParameter(value), - JsonNode value => new OpenIddictParameter(value), - bool value => new OpenIddictParameter(value), - int value => new OpenIddictParameter(value), - long value => new OpenIddictParameter(value), - string value => new OpenIddictParameter(value), - string[] value => new OpenIddictParameter(value), + + JsonElement value => new OpenIddictParameter(value), + JsonNode value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + ImmutableArray value => new OpenIddictParameter(value), + + string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)), + IEnumerable value => new OpenIddictParameter([.. value]), _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) }; diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs index 57eb3a40..50afca1a 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs @@ -61,7 +61,7 @@ public sealed class OpenIddictServerDataProtectionFormatter : IOpenIddictServerD { // Read the version of the format used to serialize the ticket. var version = reader.ReadInt32(); - if (version != 5) + if (version is not 5) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0287)); } diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs index b43071c4..4a24d218 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs @@ -104,9 +104,9 @@ public static partial class OpenIddictServerOwinHandlers var location = context.RequestUri.GetLeftPart(UriPartial.Path); foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -180,9 +180,9 @@ public static partial class OpenIddictServerOwinHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -256,9 +256,9 @@ public static partial class OpenIddictServerOwinHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -317,9 +317,9 @@ public static partial class OpenIddictServerOwinHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs index f952fb28..f32bc84f 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs @@ -84,9 +84,9 @@ public static partial class OpenIddictServerOwinHandlers var location = context.RequestUri.GetLeftPart(UriPartial.Path); foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { @@ -143,9 +143,9 @@ public static partial class OpenIddictServerOwinHandlers // For consistency, multiple parameters with the same name are also supported by this endpoint. foreach (var (key, value) in from parameter in context.Response.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() where !string.IsNullOrEmpty(value) select (parameter.Key, Value: value)) { diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs index a0ea78cc..28f2790f 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs @@ -13,6 +13,7 @@ using System.Text.Encodings.Web; using System.Text.Json; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using OpenIddict.Extensions; using Owin; using static OpenIddict.Server.Owin.OpenIddictServerOwinConstants; @@ -524,7 +525,10 @@ public static partial class OpenIddictServerOwinHandlers if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { - context.Transaction.Request = new OpenIddictRequest(request.Query); + context.Transaction.Request = new OpenIddictRequest( + from parameter in request.Query + let values = new StringValues(parameter.Value) + select new KeyValuePair(parameter.Key, values)); } else @@ -575,7 +579,10 @@ public static partial class OpenIddictServerOwinHandlers if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { - context.Transaction.Request = new OpenIddictRequest(request.Query); + context.Transaction.Request = new OpenIddictRequest( + from parameter in request.Query + let values = new StringValues(parameter.Value) + select new KeyValuePair(parameter.Key, values)); } else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase)) @@ -606,7 +613,10 @@ public static partial class OpenIddictServerOwinHandlers return; } - context.Transaction.Request = new OpenIddictRequest(await request.ReadFormAsync()); + context.Transaction.Request = new OpenIddictRequest( + from parameter in await request.ReadFormAsync() + let values = new StringValues(parameter.Value) + select new KeyValuePair(parameter.Key, values)); } else @@ -681,7 +691,10 @@ public static partial class OpenIddictServerOwinHandlers return; } - context.Transaction.Request = new OpenIddictRequest(await request.ReadFormAsync()); + context.Transaction.Request = new OpenIddictRequest( + from parameter in await request.ReadFormAsync() + let values = new StringValues(parameter.Value) + select new KeyValuePair(parameter.Key, values)); } else diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs b/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs index beefc946..bdc2c92d 100644 --- a/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs +++ b/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs @@ -6,6 +6,7 @@ using System.Security.Claims; using System.Text.Json; +using System.Text.Json.Nodes; namespace OpenIddict.Server; @@ -102,7 +103,7 @@ public static partial class OpenIddictServerEvents /// Note: this value should only be populated if the "address" /// scope was requested and accepted by the resource owner. /// - public JsonElement Address { get; set; } + public JsonObject? Address { get; set; } /// /// Gets or sets the values used for the "aud" claim. diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs index 26beb1a7..4d41e66e 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs @@ -246,20 +246,20 @@ public static partial class OpenIddictServerHandlers [Metadata.DeviceAuthorizationEndpoint] = notification.DeviceAuthorizationEndpoint?.AbsoluteUri, [Metadata.PushedAuthorizationRequestEndpoint] = notification.PushedAuthorizationEndpoint?.AbsoluteUri, [Metadata.JwksUri] = notification.JsonWebKeySetEndpoint?.AbsoluteUri, - [Metadata.GrantTypesSupported] = notification.GrantTypes.ToArray(), - [Metadata.ResponseTypesSupported] = notification.ResponseTypes.ToArray(), - [Metadata.ResponseModesSupported] = notification.ResponseModes.ToArray(), - [Metadata.ScopesSupported] = notification.Scopes.ToArray(), - [Metadata.ClaimsSupported] = notification.Claims.ToArray(), - [Metadata.IdTokenSigningAlgValuesSupported] = notification.IdTokenSigningAlgorithms.ToArray(), - [Metadata.CodeChallengeMethodsSupported] = notification.CodeChallengeMethods.ToArray(), - [Metadata.SubjectTypesSupported] = notification.SubjectTypes.ToArray(), - [Metadata.PromptValuesSupported] = notification.PromptValues.ToArray(), - [Metadata.TokenEndpointAuthMethodsSupported] = notification.TokenEndpointAuthenticationMethods.ToArray(), - [Metadata.IntrospectionEndpointAuthMethodsSupported] = notification.IntrospectionEndpointAuthenticationMethods.ToArray(), - [Metadata.RevocationEndpointAuthMethodsSupported] = notification.RevocationEndpointAuthenticationMethods.ToArray(), - [Metadata.DeviceAuthorizationEndpointAuthMethodsSupported] = notification.DeviceAuthorizationEndpointAuthenticationMethods.ToArray(), - [Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported] = notification.PushedAuthorizationEndpointAuthenticationMethods.ToArray(), + [Metadata.GrantTypesSupported] = notification.GrantTypes.ToImmutableArray(), + [Metadata.ResponseTypesSupported] = notification.ResponseTypes.ToImmutableArray(), + [Metadata.ResponseModesSupported] = notification.ResponseModes.ToImmutableArray(), + [Metadata.ScopesSupported] = notification.Scopes.ToImmutableArray(), + [Metadata.ClaimsSupported] = notification.Claims.ToImmutableArray(), + [Metadata.IdTokenSigningAlgValuesSupported] = notification.IdTokenSigningAlgorithms.ToImmutableArray(), + [Metadata.CodeChallengeMethodsSupported] = notification.CodeChallengeMethods.ToImmutableArray(), + [Metadata.SubjectTypesSupported] = notification.SubjectTypes.ToImmutableArray(), + [Metadata.PromptValuesSupported] = notification.PromptValues.ToImmutableArray(), + [Metadata.TokenEndpointAuthMethodsSupported] = notification.TokenEndpointAuthenticationMethods.ToImmutableArray(), + [Metadata.IntrospectionEndpointAuthMethodsSupported] = notification.IntrospectionEndpointAuthenticationMethods.ToImmutableArray(), + [Metadata.RevocationEndpointAuthMethodsSupported] = notification.RevocationEndpointAuthenticationMethods.ToImmutableArray(), + [Metadata.DeviceAuthorizationEndpointAuthMethodsSupported] = notification.DeviceAuthorizationEndpointAuthenticationMethods.ToImmutableArray(), + [Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported] = notification.PushedAuthorizationEndpointAuthenticationMethods.ToImmutableArray(), [Metadata.RequirePushedAuthorizationRequests] = notification.RequirePushedAuthorizationRequests }; diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs index 0d81a61b..4bf16da7 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs @@ -268,7 +268,7 @@ public static partial class OpenIddictServerHandlers break; default: - response[Claims.Audience] = notification.Audiences.ToArray(); + response[Claims.Audience] = notification.Audiences.ToImmutableArray(); break; } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs index 27ee1f28..2b348a6f 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs @@ -556,11 +556,18 @@ public static partial class OpenIddictServerHandlers // Restore the claim destinations from the special oi_cl_dstn claim (represented as a dictionary/JSON object). // // Note: starting in 7.0, Wilson no longer uses JSON.NET and supports a limited set of types for the - // TryGetPayloadValue() API. Since ImmutableDictionary is not supported, the value - // is retrieved as a Dictionary, which is supported by both Wilson 6.x and 7.x. + // TryGetPayloadValue() API. Since ImmutableDictionary is not supported, the value is + // retrieved as a Dictionary and converted to ImmutableDictionary. if (token.TryGetPayloadValue(Claims.Private.ClaimDestinationsMap, out Dictionary destinations)) { - context.Principal.SetDestinations(destinations.ToImmutableDictionary()); + var builder = ImmutableDictionary.CreateBuilder>(); + + foreach (var destination in destinations) + { + builder.Add(destination.Key, [.. destination.Value]); + } + + context.Principal.SetDestinations(builder.ToImmutable()); } context.Logger.LogTrace(SR.GetResourceString(SR.ID6001), context.Token, context.Principal.Claims); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs index 430e888f..c32344a6 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs @@ -243,7 +243,7 @@ public static partial class OpenIddictServerHandlers break; default: - response[Claims.Audience] = notification.Audiences.ToArray(); + response[Claims.Audience] = notification.Audiences.ToImmutableArray(); break; } diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs index ce37c6a6..0131b084 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; @@ -368,13 +369,17 @@ public static partial class OpenIddictValidationAspNetCoreHandlers context.Parameters[parameter.Key] = parameter.Value switch { OpenIddictParameter value => value, - JsonElement value => new OpenIddictParameter(value), - JsonNode value => new OpenIddictParameter(value), - bool value => new OpenIddictParameter(value), - int value => new OpenIddictParameter(value), - long value => new OpenIddictParameter(value), - string value => new OpenIddictParameter(value), - string[] value => new OpenIddictParameter(value), + + JsonElement value => new OpenIddictParameter(value), + JsonNode value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + ImmutableArray value => new OpenIddictParameter(value), + + string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)), + IEnumerable value => new OpenIddictParameter([.. value]), _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) }; diff --git a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs index 2a24d29b..983a982f 100644 --- a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs +++ b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs @@ -44,7 +44,7 @@ public sealed class OpenIddictValidationDataProtectionFormatter : IOpenIddictVal { // Read the version of the format used to serialize the ticket. var version = reader.ReadInt32(); - if (version != 5) + if (version is not 5) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0287)); } diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs index 8bc6698a..a872cd8e 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs @@ -568,8 +568,8 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers { request.RequestUri = OpenIddictHelpers.AddQueryStringParameters(request.RequestUri, context.Transaction.Request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)); } // For POST requests, attach the request parameters to the request form by default. @@ -577,9 +577,9 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers { request.Content = new FormUrlEncodedContent( from parameter in context.Transaction.Request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() select new KeyValuePair(parameter.Key, value)); } diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs index dad16eb0..7bedb22f 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs @@ -445,24 +445,12 @@ public static partial class OpenIddictValidationHandlers // The following parameters MUST be formatted as arrays of objects: JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element && - element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element), + element.ValueKind is JsonValueKind.Array && + OpenIddictHelpers.ValidateArrayElements(element, JsonValueKind.Object), // Parameters that are not in the well-known list can be of any type. _ => true }; - - static bool ValidateObjectArray(JsonElement element) - { - foreach (var item in element.EnumerateArray()) - { - if (item.ValueKind is not JsonValueKind.Object) - { - return false; - } - } - - return true; - } } } diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs index f4b47866..4251d81a 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs @@ -269,7 +269,7 @@ public class OpenIddictConverterTests var message = new OpenIddictMessage(); message.AddParameter("string", "value"); - message.AddParameter("array", new[] { "value" }); + message.AddParameter("array", new OpenIddictParameter(["value"])); // Act converter.Write(writer, value: message, options: null!); diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs index 317d3b7a..a3efe139 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs @@ -1125,8 +1125,8 @@ public class OpenIddictExtensionsTests // Assert Assert.Equal(2, destinations.Count); - Assert.Equal([Destinations.AccessToken, Destinations.IdentityToken], destinations[Claims.Name]); - Assert.Equal([Destinations.IdentityToken], destinations[Claims.Email]); + Assert.Equal([Destinations.AccessToken, Destinations.IdentityToken], [.. destinations[Claims.Name]]); + Assert.Equal([Destinations.IdentityToken], [.. destinations[Claims.Email]]); } [Fact] @@ -1159,8 +1159,8 @@ public class OpenIddictExtensionsTests // Assert Assert.Equal(2, destinations.Count); - Assert.Equal([Destinations.AccessToken, Destinations.IdentityToken], destinations[Claims.Name]); - Assert.Equal([Destinations.IdentityToken], destinations[Claims.Email]); + Assert.Equal([Destinations.AccessToken, Destinations.IdentityToken], [.. destinations[Claims.Name]]); + Assert.Equal([Destinations.IdentityToken], [.. destinations[Claims.Email]]); } [Fact] @@ -1192,7 +1192,7 @@ public class OpenIddictExtensionsTests { // Arrange var identity = new ClaimsIdentity(); - var destinations = (ImmutableDictionary) null!; + var destinations = (ImmutableDictionary>) null!; // Act and assert var exception = Assert.Throws(() => identity.SetDestinations(destinations)); @@ -1205,7 +1205,7 @@ public class OpenIddictExtensionsTests { // Arrange var principal = new ClaimsPrincipal(new ClaimsIdentity()); - var destinations = (ImmutableDictionary) null!; + var destinations = (ImmutableDictionary>) null!; // Act and assert var exception = Assert.Throws(() => principal.SetDestinations(destinations)); @@ -1226,7 +1226,7 @@ public class OpenIddictExtensionsTests var identity = new ClaimsIdentity(claims); - var destinations = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + var destinations = ImmutableDictionary.CreateBuilder>(StringComparer.Ordinal); destinations.Add(Claims.Name, [Destinations.AccessToken, Destinations.IdentityToken]); destinations.Add(Claims.Email, [Destinations.IdentityToken]); destinations.Add(Claims.Nonce, []); @@ -1253,7 +1253,7 @@ public class OpenIddictExtensionsTests var principal = new ClaimsPrincipal(new ClaimsIdentity(claims)); - var destinations = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + var destinations = ImmutableDictionary.CreateBuilder>(StringComparer.Ordinal); destinations.Add(Claims.Name, [Destinations.AccessToken, Destinations.IdentityToken]); destinations.Add(Claims.Email, [Destinations.IdentityToken]); destinations.Add(Claims.Nonce, []); diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs index 3f9ec073..013ce84e 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; @@ -98,7 +99,8 @@ public class OpenIddictMessageTests // Assert Assert.Equal(1, message.Count); - Assert.Equal(["Fabrikam", "Contoso"], (string?[]?) message.GetParameter("parameter")); + Assert.Equal?>(["Fabrikam", "Contoso"], + (ImmutableArray?) message.GetParameter("parameter")); } [Fact] @@ -107,12 +109,13 @@ public class OpenIddictMessageTests // Arrange and act var message = new OpenIddictMessage( [ - new KeyValuePair("parameter", ["Fabrikam", "Contoso"]) + new KeyValuePair?>("parameter", ["Fabrikam", "Contoso"]) ]); // Assert Assert.Equal(1, message.Count); - Assert.Equal(["Fabrikam", "Contoso"], (string?[]?) message.GetParameter("parameter")); + Assert.Equal?>(["Fabrikam", "Contoso"], + (ImmutableArray?) message.GetParameter("parameter")); } [Fact] @@ -121,12 +124,12 @@ public class OpenIddictMessageTests // Arrange and act var message = new OpenIddictMessage( [ - new KeyValuePair("parameter", ["Fabrikam"]) + new KeyValuePair?>("parameter", ["Fabrikam"]) ]); // Assert Assert.Equal(1, message.Count); - Assert.Equal("Fabrikam", message.GetParameter("parameter")?.Value); + Assert.Equal("Fabrikam", (string?) message.GetParameter("parameter")); } [Theory] @@ -423,7 +426,7 @@ public class OpenIddictMessageTests // Act and assert Assert.True(message.TryGetParameter("parameter", out var parameter)); - Assert.Equal(42, (long?) parameter.Value); + Assert.Equal(42, (long?) parameter); } [Fact] @@ -434,7 +437,7 @@ public class OpenIddictMessageTests // Act and assert Assert.False(message.TryGetParameter("parameter", out OpenIddictParameter parameter)); - Assert.Null(parameter.Value); + Assert.Equal(default, parameter); } [Fact] @@ -507,7 +510,7 @@ public class OpenIddictMessageTests // Arrange var message = new OpenIddictMessage { - ["redirect_uris"] = new[] { "https://abc.org/callback" }, + ["redirect_uris"] = new OpenIddictParameter(["https://abc.org/callback"]), ["client_name"] = "My Example Client" }; diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs index 81c29ed0..b837deb3 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs @@ -4,9 +4,12 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; +using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; +using Microsoft.Extensions.Primitives; using Xunit; namespace OpenIddict.Abstractions.Tests.Primitives; @@ -191,9 +194,9 @@ public class OpenIddictParameterTests var parameter = new OpenIddictParameter(["Fabrikam", "Contoso"]); // Act and assert - Assert.True(parameter.Equals(new string[] { "Fabrikam", "Contoso" })); - Assert.False(parameter.Equals(new string[] { "Fabrikam" })); - Assert.False(parameter.Equals(new string[] { "Contoso", "Fabrikam" })); + Assert.True(parameter.Equals(new OpenIddictParameter(["Fabrikam", "Contoso"]))); + Assert.False(parameter.Equals(new OpenIddictParameter(["Fabrikam"]))); + Assert.False(parameter.Equals(new OpenIddictParameter(["Contoso", "Fabrikam"]))); } [Fact] @@ -203,11 +206,11 @@ public class OpenIddictParameterTests var parameter = new OpenIddictParameter(JsonSerializer.Deserialize("[0,1,2,3]")); // Act and assert - Assert.True(parameter.Equals(JsonSerializer.Deserialize("[0,1,2,3]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("[]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("[0,1,2]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("[3,2,1,0]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("{}"))); + Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[0,1,2,3]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[0,1,2]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[3,2,1,0]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("{}")))); Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3)))); Assert.False(parameter.Equals(new OpenIddictParameter(new JsonArray()))); @@ -223,11 +226,11 @@ public class OpenIddictParameterTests var parameter = new OpenIddictParameter(new JsonArray(0, 1, 2, 3)); // Act and assert - Assert.True(parameter.Equals(JsonSerializer.Deserialize("[0,1,2,3]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("[]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("[0,1,2]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("[3,2,1,0]"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize("{}"))); + Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[0,1,2,3]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[0,1,2]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("[3,2,1,0]")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize("{}")))); Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3)))); Assert.False(parameter.Equals(new OpenIddictParameter(new JsonArray()))); @@ -243,11 +246,11 @@ public class OpenIddictParameterTests var parameter = new OpenIddictParameter(JsonSerializer.Deserialize(@"{""field"":[0,1,2,3]}")); // Act and assert - Assert.True(parameter.Equals(JsonSerializer.Deserialize(@"{""field"":[0,1,2,3]}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"{}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"{""field"":""value""}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"{""field"":[0,1,2]}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"[]"))); + Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{""field"":[0,1,2,3]}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{""field"":""value""}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{""field"":[0,1,2]}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"[]")))); Assert.True(parameter.Equals(new OpenIddictParameter(new JsonObject { @@ -285,23 +288,23 @@ public class OpenIddictParameterTests }); // Act and assert - Assert.True(parameter.Equals(JsonSerializer.Deserialize(@"{""field"":[0,1,2,3]}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"{}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"{""field"":""value""}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"{""field"":[0,1,2]}"))); - Assert.False(parameter.Equals(JsonSerializer.Deserialize(@"[]"))); + Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{""field"":[0,1,2,3]}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{""field"":""value""}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"{""field"":[0,1,2]}")))); + Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize(@"[]")))); - Assert.True(parameter.Equals(new JsonObject + Assert.True(parameter.Equals(new OpenIddictParameter(new JsonObject { ["field"] = new JsonArray(0, 1, 2, 3) - })); + }))); - Assert.False(parameter.Equals(new JsonObject())); + Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject()))); - Assert.False(parameter.Equals(new JsonObject + Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject { ["field"] = JsonValue.Create("value") - })); + }))); Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject { @@ -309,20 +312,20 @@ public class OpenIddictParameterTests @"{""field"":""value""}").GetProperty("field")) }))); - Assert.False(parameter.Equals(new JsonObject + Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject { ["field"] = new JsonArray(0, 1, 2) - })); + }))); - Assert.True(parameter.Equals(JsonValue.Create(new Dictionary + Assert.True(parameter.Equals(new OpenIddictParameter(JsonValue.Create(new Dictionary { ["field"] = new JsonArray(0, 1, 2, 3) - }))); + })))); - Assert.True(parameter.Equals(JsonValue.Create(new Dictionary + Assert.True(parameter.Equals(new OpenIddictParameter(JsonValue.Create(new Dictionary { ["field"] = new[] { 0, 1, 2, 3 } - }))); + })))); } [Fact] @@ -797,11 +800,7 @@ public class OpenIddictParameterTests public void GetNamedParameters_ReturnsEmptyDictionaryForArrays() { // Arrange - var parameters = new[] - { - "Fabrikam", - "Contoso" - }; + ImmutableArray parameters = ["Fabrikam", "Contoso"]; var parameter = new OpenIddictParameter(parameters); @@ -924,11 +923,7 @@ public class OpenIddictParameterTests public void GetUnnamedParameters_ReturnsExpectedParametersForArrays() { // Arrange - var parameters = new[] - { - "Fabrikam", - "Contoso" - }; + ImmutableArray parameters = ["Fabrikam", "Contoso"]; var parameter = new OpenIddictParameter(parameters); @@ -1024,7 +1019,7 @@ public class OpenIddictParameterTests Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((bool?) null))); Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((long?) null))); Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((string?) null))); - Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((string?[]?) null))); + Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((ImmutableArray?) null))); } [Fact] @@ -1502,11 +1497,11 @@ public class OpenIddictParameterTests public void BoolConverter_CanCreateParameterFromBooleanValue() { // Arrange, act and assert - Assert.True((bool?) new OpenIddictParameter(true).Value); - Assert.True((bool?) new OpenIddictParameter((bool?) true).Value); + Assert.True((bool?) new OpenIddictParameter(true)); + Assert.True((bool?) new OpenIddictParameter((bool?) true)); - Assert.False((bool?) new OpenIddictParameter(false).Value); - Assert.False((bool?) new OpenIddictParameter((bool?) false).Value); + Assert.False((bool?) new OpenIddictParameter(false)); + Assert.False((bool?) new OpenIddictParameter((bool?) false)); } [Fact] @@ -1898,8 +1893,8 @@ public class OpenIddictParameterTests public void LongConverter_CanCreateParameterFromLongValue() { // Arrange, act and assert - Assert.Equal(42, (long?) new OpenIddictParameter(42).Value); - Assert.Equal(42, (long?) new OpenIddictParameter((long?) 42).Value); + Assert.Equal(42, (long?) new OpenIddictParameter(42)); + Assert.Equal(42, (long?) new OpenIddictParameter((long?) 42)); } [Fact] @@ -1970,7 +1965,7 @@ public class OpenIddictParameterTests public void StringConverter_CanCreateParameterFromStringValue() { // Arrange, act and assert - Assert.Equal("Fabrikam", (string?) new OpenIddictParameter("Fabrikam").Value); + Assert.Equal("Fabrikam", (string?) new OpenIddictParameter("Fabrikam")); } [Fact] @@ -2041,77 +2036,155 @@ public class OpenIddictParameterTests public void StringArrayConverter_CanCreateParameterFromArray() { // Arrange - var array = new[] { "Fabrikam", "Contoso" }; + string?[] array = ["Fabrikam", "Contoso"]; // Act - var parameter = new OpenIddictParameter(array); + var parameter = new OpenIddictParameter([.. array]); // Assert - Assert.Same(array, parameter.Value); + Assert.Equal?>(array, (ImmutableArray?) parameter); } [Fact] public void StringArrayConverter_CanCreateParameterFromPrimitiveValues() { // Arrange, act and assert - Assert.Equal(["Fabrikam"], (string?[]?) new OpenIddictParameter("Fabrikam")); - Assert.Equal(["false"], (string?[]?) new OpenIddictParameter(false)); - Assert.Equal(["42"], (string?[]?) new OpenIddictParameter(42)); + Assert.Equal?>(["Fabrikam"], (ImmutableArray?) new OpenIddictParameter("Fabrikam")); + Assert.Equal?>(["false"], (ImmutableArray?) new OpenIddictParameter(false)); + Assert.Equal?>(["42"], (ImmutableArray?) new OpenIddictParameter(42)); } [Fact] public void StringArrayConverter_ReturnsDefaultValueForNullValues() { // Arrange, act and assert - Assert.Null((string?[]?) new OpenIddictParameter()); + Assert.Null((ImmutableArray?) new OpenIddictParameter()); } [Fact] public void StringArrayConverter_ReturnsSingleElementArrayForStringValue() { // Arrange, act and assert - Assert.Equal(["Fabrikam"], (string?[]?) new OpenIddictParameter("Fabrikam")); + Assert.Equal?>(["Fabrikam"], (ImmutableArray?) new OpenIddictParameter("Fabrikam")); } [Fact] public void StringArrayConverter_ReturnsDefaultValueForUnsupportedJsonValues() { // Arrange, act and assert - Assert.Null((string?[]?) new OpenIddictParameter(new JsonElement())); - Assert.Null((string?[]?) new OpenIddictParameter( + Assert.Null((ImmutableArray?) new OpenIddictParameter(new JsonElement())); + Assert.Null((ImmutableArray?) new OpenIddictParameter( JsonSerializer.Deserialize(@"[""value"",[]]"))); - Assert.Null((string?[]?) new OpenIddictParameter( + Assert.Null((ImmutableArray?) new OpenIddictParameter( JsonSerializer.Deserialize(@"[""value"",{}]"))); - Assert.Null((string?[]?) new OpenIddictParameter((JsonNode?) null)); - Assert.Null((string?[]?) new OpenIddictParameter(new JsonArray("value", new JsonArray()))); - Assert.Null((string?[]?) new OpenIddictParameter(new JsonArray("value", new JsonObject()))); + Assert.Null((ImmutableArray?) new OpenIddictParameter((JsonNode?) null)); + Assert.Null((ImmutableArray?) new OpenIddictParameter(new JsonArray("value", new JsonArray()))); + Assert.Null((ImmutableArray?) new OpenIddictParameter(new JsonArray("value", new JsonObject()))); } [Fact] public void StringArrayConverter_CanConvertFromJsonValues() { // Arrange, act and assert - Assert.Equal(["Fabrikam"], (string?[]?) new OpenIddictParameter( + Assert.Equal?>(["Fabrikam"], (ImmutableArray?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"{""field"":""Fabrikam""}").GetProperty("field"))); + Assert.Equal?>(["false"], (ImmutableArray?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"{""field"":false}").GetProperty("field"))); + Assert.Equal?>(["42"], (ImmutableArray?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"{""field"":42}").GetProperty("field"))); + Assert.Equal?>(["Fabrikam"], (ImmutableArray?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"[""Fabrikam""]"))); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"[""Contoso"",""Fabrikam""]"))); + Assert.Equal?>(["value", "42", "true"], (ImmutableArray?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"[""value"",42,true]"))); + + Assert.Equal?>(["Fabrikam"], (ImmutableArray?) new OpenIddictParameter(JsonValue.Create("Fabrikam"))); + Assert.Equal?>(["false"], (ImmutableArray?) new OpenIddictParameter(JsonValue.Create(false))); + Assert.Equal?>(["42"], (ImmutableArray?) new OpenIddictParameter(JsonValue.Create(42))); + Assert.Equal?>(["42"], (ImmutableArray?) new OpenIddictParameter(JsonValue.Create(42L))); + Assert.Equal?>(["Fabrikam"], (ImmutableArray?) new OpenIddictParameter(new JsonArray("Fabrikam"))); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam"))); + Assert.Equal?>(["value", "42", "true"], (ImmutableArray?) new OpenIddictParameter(new JsonArray("value", 42, true))); + } + + [Fact] + public void StringValuesConverter_CanCreateParameterFromArray() + { + // Arrange + string?[] array = ["Fabrikam", "Contoso"]; + + // Act + var parameter = new OpenIddictParameter([.. array]); + + // Assert + Assert.Equal?>(array, (StringValues?) parameter); + } + + [Fact] + public void StringValuesConverter_CanCreateParameterFromPrimitiveValues() + { + // Arrange, act and assert + Assert.Equal?>(["Fabrikam"], (StringValues?) new OpenIddictParameter("Fabrikam")); + Assert.Equal?>(["false"], (StringValues?) new OpenIddictParameter(false)); + Assert.Equal?>(["42"], (StringValues?) new OpenIddictParameter(42)); + } + + [Fact] + public void StringValuesConverter_ReturnsDefaultValueForNullValues() + { + // Arrange, act and assert + Assert.Null((StringValues?) new OpenIddictParameter()); + } + + [Fact] + public void StringValuesConverter_ReturnsSingleElementArrayForStringValue() + { + // Arrange, act and assert + Assert.Equal?>(["Fabrikam"], (StringValues?) new OpenIddictParameter("Fabrikam")); + } + + [Fact] + public void StringValuesConverter_ReturnsDefaultValueForUnsupportedJsonValues() + { + // Arrange, act and assert + Assert.Null((StringValues?) new OpenIddictParameter(new JsonElement())); + Assert.Null((StringValues?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"[""value"",[]]"))); + + Assert.Null((StringValues?) new OpenIddictParameter( + JsonSerializer.Deserialize(@"[""value"",{}]"))); + + Assert.Null((StringValues?) new OpenIddictParameter((JsonNode?) null)); + Assert.Null((StringValues?) new OpenIddictParameter(new JsonArray("value", new JsonArray()))); + Assert.Null((StringValues?) new OpenIddictParameter(new JsonArray("value", new JsonObject()))); + } + + [Fact] + public void StringValuesConverter_CanConvertFromJsonValues() + { + // Arrange, act and assert + Assert.Equal?>(["Fabrikam"], (StringValues?) new OpenIddictParameter( JsonSerializer.Deserialize(@"{""field"":""Fabrikam""}").GetProperty("field"))); - Assert.Equal(["false"], (string?[]?) new OpenIddictParameter( + Assert.Equal?>(["false"], (StringValues?) new OpenIddictParameter( JsonSerializer.Deserialize(@"{""field"":false}").GetProperty("field"))); - Assert.Equal(["42"], (string?[]?) new OpenIddictParameter( + Assert.Equal?>(["42"], (StringValues?) new OpenIddictParameter( JsonSerializer.Deserialize(@"{""field"":42}").GetProperty("field"))); - Assert.Equal(["Fabrikam"], (string?[]?) new OpenIddictParameter( + Assert.Equal?>(["Fabrikam"], (StringValues?) new OpenIddictParameter( JsonSerializer.Deserialize(@"[""Fabrikam""]"))); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) new OpenIddictParameter( + Assert.Equal?>(["Contoso", "Fabrikam"], (StringValues?) new OpenIddictParameter( JsonSerializer.Deserialize(@"[""Contoso"",""Fabrikam""]"))); - Assert.Equal(["value", "42", "true"], (string?[]?) new OpenIddictParameter( + Assert.Equal?>(["value", "42", "true"], (StringValues?) new OpenIddictParameter( JsonSerializer.Deserialize(@"[""value"",42,true]"))); - Assert.Equal(["Fabrikam"], (string?[]?) new OpenIddictParameter(JsonValue.Create("Fabrikam"))); - Assert.Equal(["false"], (string?[]?) new OpenIddictParameter(JsonValue.Create(false))); - Assert.Equal(["42"], (string?[]?) new OpenIddictParameter(JsonValue.Create(42))); - Assert.Equal(["42"], (string?[]?) new OpenIddictParameter(JsonValue.Create(42L))); - Assert.Equal(["Fabrikam"], (string?[]?) new OpenIddictParameter(new JsonArray("Fabrikam"))); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam"))); - Assert.Equal(["value", "42", "true"], (string?[]?) new OpenIddictParameter(new JsonArray("value", 42, true))); + Assert.Equal?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(JsonValue.Create("Fabrikam"))); + Assert.Equal?>(["false"], (StringValues?) new OpenIddictParameter(JsonValue.Create(false))); + Assert.Equal?>(["42"], (StringValues?) new OpenIddictParameter(JsonValue.Create(42))); + Assert.Equal?>(["42"], (StringValues?) new OpenIddictParameter(JsonValue.Create(42L))); + Assert.Equal?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(new JsonArray("Fabrikam"))); + Assert.Equal?>(["Contoso", "Fabrikam"], (StringValues?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam"))); + Assert.Equal?>(["value", "42", "true"], (StringValues?) new OpenIddictParameter(new JsonArray("value", 42, true))); } } diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictRequestTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictRequestTests.cs index c6859ede..520c4510 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictRequestTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictRequestTests.cs @@ -5,7 +5,9 @@ * the license and the contributors participating to this project. */ -using System.Text.Json; +using System.Collections.Immutable; +using System.Runtime.InteropServices; +using System.Text.Json.Nodes; using Xunit; namespace OpenIddict.Abstractions.Tests.Primitives; @@ -48,7 +50,7 @@ public class OpenIddictRequestTests { /* property: */ nameof(OpenIddictRequest.Claims), /* name: */ Parameters.Claims, - /* value: */ new OpenIddictParameter(JsonSerializer.Deserialize(@"{""userinfo"": {}}")) + /* value: */ new OpenIddictParameter(JsonObject.Parse(@"{""userinfo"": {}}")) }; yield return new object[] @@ -216,7 +218,7 @@ public class OpenIddictRequestTests { /* property: */ nameof(OpenIddictRequest.Registration), /* name: */ Parameters.Registration, - /* value: */ new OpenIddictParameter(JsonSerializer.Deserialize(@"{""policy_uri"": ""http://www.fabrikam.com/policy""}")) + /* value: */ new OpenIddictParameter(JsonObject.Parse(@"{""policy_uri"": ""http://www.fabrikam.com/policy""}")) }; yield return new object[] @@ -314,7 +316,16 @@ public class OpenIddictRequestTests request.SetParameter(name, value); // Act and assert - Assert.Equal(value.Value, typeof(OpenIddictRequest).GetProperty(property)!.GetValue(request)); + var info = typeof(OpenIddictRequest).GetProperty(property)!; + if (typeof(JsonNode).IsAssignableFrom(info.PropertyType)) + { + Assert.True(JsonNode.DeepEquals((JsonNode?) value.GetRawValue(), (JsonNode?) info.GetValue(request))); + } + + else + { + Assert.Equal(value.GetRawValue(), info.GetValue(request)); + } } [Theory] @@ -325,7 +336,16 @@ public class OpenIddictRequestTests var request = new OpenIddictRequest(); // Act - typeof(OpenIddictRequest).GetProperty(property)!.SetValue(request, value.Value); + var info = typeof(OpenIddictRequest).GetProperty(property)!; + if (typeof(ImmutableArray).IsAssignableFrom(info.PropertyType)) + { + info.SetValue(request, ImmutableCollectionsMarshal.AsImmutableArray((string?[]?) value.GetRawValue())); + } + + else + { + info.SetValue(request, value.GetRawValue()); + } // Assert Assert.Equal(value, request.GetParameter(name)); diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictResponseTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictResponseTests.cs index d55e4893..37dd4be8 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictResponseTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictResponseTests.cs @@ -5,6 +5,9 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; +using System.Runtime.InteropServices; +using System.Text.Json.Nodes; using Xunit; namespace OpenIddict.Abstractions.Tests.Primitives; @@ -145,7 +148,16 @@ public class OpenIddictResponseTests response.SetParameter(name, value); // Act and assert - Assert.Equal(value.Value, typeof(OpenIddictResponse).GetProperty(property)!.GetValue(response)); + var info = typeof(OpenIddictResponse).GetProperty(property)!; + if (typeof(JsonNode).IsAssignableFrom(info.PropertyType)) + { + Assert.True(JsonNode.DeepEquals((JsonNode?) value.GetRawValue(), (JsonNode?) info.GetValue(response))); + } + + else + { + Assert.Equal(value.GetRawValue(), info.GetValue(response)); + } } [Theory] @@ -156,7 +168,16 @@ public class OpenIddictResponseTests var response = new OpenIddictResponse(); // Act - typeof(OpenIddictResponse).GetProperty(property)!.SetValue(response, value.Value); + var info = typeof(OpenIddictResponse).GetProperty(property)!; + if (typeof(ImmutableArray).IsAssignableFrom(info.PropertyType)) + { + info.SetValue(response, ImmutableCollectionsMarshal.AsImmutableArray((string?[]?) value.GetRawValue())); + } + + else + { + info.SetValue(response, value.GetRawValue()); + } // Assert Assert.Equal(value, response.GetParameter(name)); diff --git a/test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs b/test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs index dbec364b..84b00eb4 100644 --- a/test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs +++ b/test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Security.Claims; using System.Text.Json; using System.Text.Json.Nodes; @@ -257,11 +258,11 @@ public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServ Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["array_parameter"]); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["node_array_parameter"]); Assert.IsType((JsonNode?) response["node_array_parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.IsType((JsonNode?) response["node_object_parameter"]); @@ -488,11 +489,11 @@ public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServ Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["array_parameter"]); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["node_array_parameter"]); Assert.IsType((JsonNode?) response["node_array_parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.IsType((JsonNode?) response["node_object_parameter"]); @@ -741,8 +742,8 @@ public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServ } var claims = result.Principal.Claims.GroupBy(claim => claim.Type) - .Select(group => new KeyValuePair( - group.Key, group.Select(claim => claim.Value).ToArray())); + .Select(group => new KeyValuePair?>( + group.Key, group.Select(claim => claim.Value).ToImmutableArray())); context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims))); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs index 06f90f64..90fffa94 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Net.Http; using System.Net.Http.Json; using AngleSharp.Html.Parser; @@ -245,17 +246,17 @@ public class OpenIddictServerIntegrationTestClient : IAsyncDisposable { message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!, request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)); } if (message.Method != HttpMethod.Get) { message.Content = new FormUrlEncodedContent( from parameter in request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() select new KeyValuePair(parameter.Key, value)); } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs index 82245903..74bd0a8d 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs @@ -2514,11 +2514,7 @@ public abstract partial class OpenIddictServerIntegrationTests .SetClaim(Claims.Subject, "Bob le Magnifique"); context.Parameters["custom_parameter"] = "custom_value"; - context.Parameters["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -2541,7 +2537,7 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Null(response.ErrorUri); Assert.NotNull(response.AccessToken); Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Theory] @@ -2660,11 +2656,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -2683,7 +2675,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] @@ -5204,11 +5196,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Parameters["custom_parameter"] = "custom_value"; - context.Parameters["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -5231,7 +5219,7 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Null(response.ErrorUri); Assert.NotNull(response.RequestUri); Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] @@ -5283,11 +5271,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -5306,6 +5290,6 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs index 0c094227..10674b89 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs @@ -1027,11 +1027,7 @@ public abstract partial class OpenIddictServerIntegrationTests context.Principal = new ClaimsPrincipal(new ClaimsIdentity()); context.Parameters["custom_parameter"] = "custom_value"; - context.Parameters["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1051,7 +1047,7 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Null(response.ErrorUri); Assert.NotNull(response.DeviceCode); Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] @@ -1116,11 +1112,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1136,7 +1128,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Theory] @@ -1467,11 +1459,7 @@ public abstract partial class OpenIddictServerIntegrationTests .SetClaim(Claims.Subject, "Bob le Magnifique"); context.Parameters["custom_parameter"] = "custom_value"; - context.Parameters["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1496,7 +1484,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs index 5dc941db..042de174 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Net.Http; using System.Security.Cryptography; using System.Text.Json; @@ -423,11 +424,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var methods = (string?[]?) response[Metadata.TokenEndpointAuthMethodsSupported]; + var methods = (ImmutableArray?) response[Metadata.TokenEndpointAuthMethodsSupported]; // Assert Assert.NotNull(methods); - Assert.Equal(3, methods.Length); + Assert.Equal(3, methods.Value.Length); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains("custom", methods); @@ -465,11 +466,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var methods = (string?[]?) response[Metadata.IntrospectionEndpointAuthMethodsSupported]; + var methods = (ImmutableArray?) response[Metadata.IntrospectionEndpointAuthMethodsSupported]; // Assert Assert.NotNull(methods); - Assert.Equal(3, methods.Length); + Assert.Equal(3, methods.Value.Length); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains("custom", methods); @@ -507,11 +508,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var methods = (string?[]?) response[Metadata.RevocationEndpointAuthMethodsSupported]; + var methods = (ImmutableArray?) response[Metadata.RevocationEndpointAuthMethodsSupported]; // Assert Assert.NotNull(methods); - Assert.Equal(3, methods.Length); + Assert.Equal(3, methods.Value.Length); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains("custom", methods); @@ -550,11 +551,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var methods = (string?[]?) response[Metadata.DeviceAuthorizationEndpointAuthMethodsSupported]; + var methods = (ImmutableArray?) response[Metadata.DeviceAuthorizationEndpointAuthMethodsSupported]; // Assert Assert.NotNull(methods); - Assert.Equal(3, methods.Length); + Assert.Equal(3, methods.Value.Length); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains("custom", methods); @@ -592,11 +593,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var methods = (string?[]?) response[Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported]; + var methods = (ImmutableArray?) response[Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported]; // Assert Assert.NotNull(methods); - Assert.Equal(3, methods.Length); + Assert.Equal(3, methods.Value.Length); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains("custom", methods); @@ -617,11 +618,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var types = (string?[]?) response[Metadata.GrantTypesSupported]; + var types = (ImmutableArray?) response[Metadata.GrantTypesSupported]; // Assert Assert.NotNull(types); - Assert.Equal(2, types.Length); + Assert.Equal(2, types.Value.Length); Assert.Contains(GrantTypes.AuthorizationCode, types); Assert.Contains(GrantTypes.Password, types); } @@ -659,11 +660,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var methods = (string?[]?) response[Metadata.CodeChallengeMethodsSupported]; + var methods = (ImmutableArray?) response[Metadata.CodeChallengeMethodsSupported]; // Assert Assert.NotNull(methods); - Assert.Equal(2, methods.Length); + Assert.Equal(2, methods.Value.Length); Assert.Contains(CodeChallengeMethods.Sha256, methods); Assert.Contains(CodeChallengeMethods.Plain, methods); } @@ -701,11 +702,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var modes = (string?[]?) response[Metadata.ResponseModesSupported]; + var modes = (ImmutableArray?) response[Metadata.ResponseModesSupported]; // Assert Assert.NotNull(modes); - Assert.Equal(2, modes.Length); + Assert.Equal(2, modes.Value.Length); Assert.Contains(ResponseModes.FormPost, modes); Assert.Contains(ResponseModes.Fragment, modes); } @@ -743,11 +744,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var types = (string?[]?) response[Metadata.ResponseTypesSupported]; + var types = (ImmutableArray?) response[Metadata.ResponseTypesSupported]; // Assert Assert.NotNull(types); - Assert.Equal(2, types.Length); + Assert.Equal(2, types.Value.Length); Assert.Contains(ResponseTypes.Code, types); Assert.Contains(ResponseTypes.Code + ' ' + ResponseTypes.IdToken, types); } @@ -785,11 +786,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var scopes = (string?[]?) response[Metadata.ScopesSupported]; + var scopes = (ImmutableArray?) response[Metadata.ScopesSupported]; // Assert Assert.NotNull(scopes); - Assert.Equal(2, scopes.Length); + Assert.Equal(2, scopes.Value.Length); Assert.Contains(Scopes.OpenId, scopes); Assert.Contains("custom_scope", scopes); } @@ -827,11 +828,11 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var claims = (string?[]?) response[Metadata.ClaimsSupported]; + var claims = (ImmutableArray?) response[Metadata.ClaimsSupported]; // Assert Assert.NotNull(claims); - Assert.Equal(2, claims.Length); + Assert.Equal(2, claims.Value.Length); Assert.Contains(Claims.Profile, claims); Assert.Contains("custom_claim", claims); } @@ -850,7 +851,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var types = (string?[]?) response[Metadata.SubjectTypesSupported]; + var types = (ImmutableArray?) response[Metadata.SubjectTypesSupported]; // Assert Assert.NotNull(types); @@ -874,7 +875,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var types = (string?[]?) response[Metadata.PromptValuesSupported]; + var types = (ImmutableArray?) response[Metadata.PromptValuesSupported]; // Assert Assert.NotNull(types); @@ -905,7 +906,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var algorithms = (string?[]?) response[Metadata.IdTokenSigningAlgValuesSupported]; + var algorithms = (ImmutableArray?) response[Metadata.IdTokenSigningAlgValuesSupported]; // Assert Assert.NotNull(algorithms); @@ -927,7 +928,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var algorithms = (string?[]?) response[Metadata.IdTokenSigningAlgValuesSupported]; + var algorithms = (ImmutableArray?) response[Metadata.IdTokenSigningAlgValuesSupported]; // Assert Assert.NotNull(algorithms); @@ -953,7 +954,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Act var response = await client.GetAsync("/.well-known/openid-configuration"); - var algorithms = (string?[]?) response[Metadata.IdTokenSigningAlgValuesSupported]; + var algorithms = (ImmutableArray?) response[Metadata.IdTokenSigningAlgValuesSupported]; // Assert Assert.NotNull(algorithms); @@ -1131,11 +1132,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1629,11 +1626,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1646,6 +1639,6 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs index 5cd286bf..ec2d4018 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs @@ -4258,11 +4258,7 @@ public abstract partial class OpenIddictServerIntegrationTests .SetClaim(Claims.Subject, "Bob le Magnifique"); context.Parameters["custom_parameter"] = "custom_value"; - context.Parameters["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -4284,7 +4280,7 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Null(response.ErrorUri); Assert.NotNull(response.AccessToken); Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] @@ -4353,11 +4349,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -4375,6 +4367,6 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs index a9ffdaf8..ccad52d2 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs @@ -1048,7 +1048,7 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Equal(JsonValueKind.True, ((JsonElement) response["boolean_claim"]).ValueKind); Assert.Equal(42, (long) response["integer_claim"]); Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_claim"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["array_claim"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["array_claim"]); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_claim"]).ValueKind); Assert.Equal("value", (string?) response["object_claim"]?["parameter"]); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_claim"]).ValueKind); @@ -1815,11 +1815,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1835,6 +1831,6 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs index ea6da45f..5db385c9 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs @@ -316,8 +316,8 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); - Assert.Equal(["Fabrikam", "Contoso"], (string?[]?) response[Claims.Audience]); - Assert.Equal(["Fabrikam", "Contoso"], (string?[]?) response[Claims.Private.Audience]); + Assert.Equal?>(["Fabrikam", "Contoso"], (ImmutableArray?) response[Claims.Audience]); + Assert.Equal?>(["Fabrikam", "Contoso"], (ImmutableArray?) response[Claims.Private.Audience]); } [Fact] @@ -415,7 +415,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); - Assert.Equal([Scopes.OpenId, Scopes.Profile], (string?[]?) response[Claims.Private.Scope]); + Assert.Equal?>([Scopes.OpenId, Scopes.Profile], (ImmutableArray?) response[Claims.Private.Scope]); } [Fact] @@ -464,7 +464,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); - Assert.Equal([Scopes.OpenId, Scopes.Profile], (string?[]?) response[Claims.Private.Scope]); + Assert.Equal?>([Scopes.OpenId, Scopes.Profile], (ImmutableArray?) response[Claims.Private.Scope]); } [Fact] diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs index 5e2cb3dd..2f3a783b 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Net.Http; using System.Security.Claims; using Microsoft.Extensions.DependencyInjection; @@ -1146,11 +1147,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1166,6 +1163,6 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs index 9e28d10b..878c9952 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs @@ -1044,11 +1044,7 @@ public abstract partial class OpenIddictServerIntegrationTests context.SignOut(); context.Parameters["custom_parameter"] = "custom_value"; - context.Parameters["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1064,7 +1060,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] @@ -1126,11 +1122,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -1146,7 +1138,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } [Fact] diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs index 49c6f673..2716e7d2 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs @@ -387,7 +387,7 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Equal(3, response.Count); Assert.Equal("http://localhost/", (string?) response[Claims.Issuer]); Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); - Assert.Equal(["Fabrikam", "Contoso"], (string?[]?) response[Claims.Audience]); + Assert.Equal?>(["Fabrikam", "Contoso"], (ImmutableArray?) response[Claims.Audience]); } [Fact] @@ -788,11 +788,7 @@ public abstract partial class OpenIddictServerIntegrationTests builder.UseInlineHandler(context => { context.Response["custom_parameter"] = "custom_value"; - context.Response["parameter_with_multiple_values"] = new[] - { - "custom_value_1", - "custom_value_2" - }; + context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]); return default; })); @@ -808,6 +804,6 @@ public abstract partial class OpenIddictServerIntegrationTests // Assert Assert.Equal("custom_value", (string?) response["custom_parameter"]); - Assert.Equal(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); + Assert.Equal?>(["custom_value_1", "custom_value_2"], (ImmutableArray?) response["parameter_with_multiple_values"]); } } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs index 16237d28..8e7cf04d 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs @@ -1456,11 +1456,11 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["array_parameter"]); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["node_array_parameter"]); Assert.IsType((JsonNode?) response["node_array_parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.IsType((JsonNode?) response["node_object_parameter"]); @@ -3899,11 +3899,11 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["array_parameter"]); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); + Assert.Equal?>(["Contoso", "Fabrikam"], (ImmutableArray?) response["node_array_parameter"]); Assert.IsType((JsonNode?) response["node_array_parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.IsType((JsonNode?) response["node_object_parameter"]); diff --git a/test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs b/test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs index ddc488e5..e9deeaab 100644 --- a/test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs +++ b/test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Security.Claims; using System.Text.Json; using Microsoft.Extensions.DependencyInjection; @@ -254,7 +255,7 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["json_parameter"]); + Assert.Equal(["Contoso", "Fabrikam"], (ImmutableArray?) response["json_parameter"]); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["json_parameter"]).ValueKind); } @@ -479,7 +480,7 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); - Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["json_parameter"]); + Assert.Equal(["Contoso", "Fabrikam"], (ImmutableArray?) response["json_parameter"]); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["json_parameter"]).ValueKind); } @@ -697,8 +698,8 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte } var claims = result.Identity.Claims.GroupBy(claim => claim.Type) - .Select(group => new KeyValuePair( - group.Key, group.Select(claim => claim.Value).ToArray())); + .Select(group => new KeyValuePair?>( + group.Key, group.Select(claim => claim.Value).ToImmutableArray())); context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims))); diff --git a/test/OpenIddict.Validation.AspNetCore.IntegrationTests/OpenIddictValidationAspNetCoreIntegrationTests.cs b/test/OpenIddict.Validation.AspNetCore.IntegrationTests/OpenIddictValidationAspNetCoreIntegrationTests.cs index c1f327d6..789905f4 100644 --- a/test/OpenIddict.Validation.AspNetCore.IntegrationTests/OpenIddictValidationAspNetCoreIntegrationTests.cs +++ b/test/OpenIddict.Validation.AspNetCore.IntegrationTests/OpenIddictValidationAspNetCoreIntegrationTests.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Security.Claims; using System.Text.Json; using Microsoft.AspNetCore.Authentication; @@ -184,8 +185,8 @@ public partial class OpenIddictValidationAspNetCoreIntegrationTests : OpenIddict } var claims = result.Principal.Claims.GroupBy(claim => claim.Type) - .Select(group => new KeyValuePair( - group.Key, group.Select(claim => claim.Value).ToArray())); + .Select(group => new KeyValuePair?>( + group.Key, group.Select(claim => claim.Value).ToImmutableArray())); context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims))); diff --git a/test/OpenIddict.Validation.IntegrationTests/OpenIddictValidationIntegrationTestClient.cs b/test/OpenIddict.Validation.IntegrationTests/OpenIddictValidationIntegrationTestClient.cs index b7ace6fb..1435f073 100644 --- a/test/OpenIddict.Validation.IntegrationTests/OpenIddictValidationIntegrationTestClient.cs +++ b/test/OpenIddict.Validation.IntegrationTests/OpenIddictValidationIntegrationTestClient.cs @@ -4,8 +4,10 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Net.Http; using System.Net.Http.Json; +using System.Runtime.InteropServices; using AngleSharp.Html.Parser; using Microsoft.Extensions.Primitives; using OpenIddict.Extensions; @@ -245,17 +247,17 @@ public class OpenIddictValidationIntegrationTestClient : IAsyncDisposable { message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!, request.GetParameters().ToDictionary( - parameter => parameter.Key, - parameter => new StringValues((string?[]?) parameter.Value))); + static parameter => parameter.Key, + static parameter => (StringValues) parameter.Value)); } if (message.Method != HttpMethod.Get) { message.Content = new FormUrlEncodedContent( from parameter in request.GetParameters() - let values = (string?[]?) parameter.Value + let values = (ImmutableArray?) parameter.Value where values is not null - from value in values + from value in values.GetValueOrDefault() select new KeyValuePair(parameter.Key, value)); } diff --git a/test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs b/test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs index 4297f7d2..e2a11407 100644 --- a/test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs +++ b/test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using System.Collections.Immutable; using System.Security.Claims; using System.Text.Json; using Microsoft.Extensions.DependencyInjection; @@ -169,8 +170,8 @@ public partial class OpenIddictValidationOwinIntegrationTests : OpenIddictValida } var claims = result.Identity.Claims.GroupBy(claim => claim.Type) - .Select(group => new KeyValuePair( - group.Key, group.Select(claim => claim.Value).ToArray())); + .Select(group => new KeyValuePair?>( + group.Key, group.Select(claim => claim.Value).ToImmutableArray())); context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims)));