Browse Source

Update OpenIddictParameter to offer ImmutableArray<string> conversions instead of string[] and clone JsonNode objects to guarantee immutability

pull/2274/head
Kévin Chalet 1 year ago
parent
commit
38e84b862d
  1. 2
      Directory.Build.targets
  2. 14
      src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
  3. 40
      src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs
  4. 314
      src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
  5. 19
      src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs
  6. 3
      src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs
  7. 8
      src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Authentication.cs
  8. 8
      src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Session.cs
  9. 37
      src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs
  10. 2
      src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionFormatter.cs
  11. 4
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Authentication.cs
  12. 4
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Session.cs
  13. 11
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs
  14. 5
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs
  15. 44
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs
  16. 44
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs
  17. 3
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs
  18. 14
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs
  19. 8
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs
  20. 16
      src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs
  21. 24
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs
  22. 16
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs
  23. 55
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs
  24. 2
      src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs
  25. 16
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs
  26. 8
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs
  27. 21
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs
  28. 3
      src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs
  29. 28
      src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs
  30. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
  31. 13
      src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs
  32. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs
  33. 19
      src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs
  34. 2
      src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs
  35. 8
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
  36. 16
      src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs
  37. 2
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs
  38. 16
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs
  39. 19
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs
  40. 231
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs
  41. 30
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictRequestTests.cs
  42. 25
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictResponseTests.cs
  43. 13
      test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs
  44. 9
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs
  45. 32
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs
  46. 24
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs
  47. 69
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs
  48. 16
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
  49. 10
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
  50. 8
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs
  51. 9
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
  52. 16
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs
  53. 10
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs
  54. 8
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
  55. 9
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs
  56. 5
      test/OpenIddict.Validation.AspNetCore.IntegrationTests/OpenIddictValidationAspNetCoreIntegrationTests.cs
  57. 10
      test/OpenIddict.Validation.IntegrationTests/OpenIddictValidationIntegrationTestClient.cs
  58. 5
      test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs

2
Directory.Build.targets

@ -100,6 +100,8 @@
<PropertyGroup <PropertyGroup
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '9.0')) "> Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '9.0')) ">
<DefineConstants>$(DefineConstants);SUPPORTS_CERTIFICATE_LOADER</DefineConstants> <DefineConstants>$(DefineConstants);SUPPORTS_CERTIFICATE_LOADER</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_JSON_ELEMENT_DEEP_EQUALS</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_JSON_ELEMENT_PROPERTY_COUNT</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup <PropertyGroup

14
src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs

@ -639,14 +639,14 @@ public static class OpenIddictExtensions
/// </summary> /// </summary>
/// <param name="identity">The identity.</param> /// <param name="identity">The identity.</param>
/// <returns>The destinations, returned as a flattened dictionary.</returns> /// <returns>The destinations, returned as a flattened dictionary.</returns>
public static ImmutableDictionary<string, string[]> GetDestinations(this ClaimsIdentity identity) public static ImmutableDictionary<string, ImmutableArray<string>> GetDestinations(this ClaimsIdentity identity)
{ {
if (identity is null) if (identity is null)
{ {
throw new ArgumentNullException(nameof(identity)); throw new ArgumentNullException(nameof(identity));
} }
var builder = ImmutableDictionary.CreateBuilder<string, string[]>(StringComparer.Ordinal); var builder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(StringComparer.Ordinal);
foreach (var group in identity.Claims.GroupBy(claim => claim.Type)) foreach (var group in identity.Claims.GroupBy(claim => claim.Type))
{ {
@ -676,14 +676,14 @@ public static class OpenIddictExtensions
/// </summary> /// </summary>
/// <param name="principal">The principal.</param> /// <param name="principal">The principal.</param>
/// <returns>The destinations, returned as a flattened dictionary.</returns> /// <returns>The destinations, returned as a flattened dictionary.</returns>
public static ImmutableDictionary<string, string[]> GetDestinations(this ClaimsPrincipal principal) public static ImmutableDictionary<string, ImmutableArray<string>> GetDestinations(this ClaimsPrincipal principal)
{ {
if (principal is null) if (principal is null)
{ {
throw new ArgumentNullException(nameof(principal)); throw new ArgumentNullException(nameof(principal));
} }
var builder = ImmutableDictionary.CreateBuilder<string, string[]>(StringComparer.Ordinal); var builder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(StringComparer.Ordinal);
foreach (var group in principal.Claims.GroupBy(claim => claim.Type)) foreach (var group in principal.Claims.GroupBy(claim => claim.Type))
{ {
@ -714,7 +714,8 @@ public static class OpenIddictExtensions
/// <param name="identity">The identity.</param> /// <param name="identity">The identity.</param>
/// <param name="destinations">The destinations, as a flattened dictionary.</param> /// <param name="destinations">The destinations, as a flattened dictionary.</param>
/// <returns>The identity.</returns> /// <returns>The identity.</returns>
public static ClaimsIdentity SetDestinations(this ClaimsIdentity identity, ImmutableDictionary<string, string[]> destinations) public static ClaimsIdentity SetDestinations(this ClaimsIdentity identity,
ImmutableDictionary<string, ImmutableArray<string>> destinations)
{ {
if (identity is null) if (identity is null)
{ {
@ -743,7 +744,8 @@ public static class OpenIddictExtensions
/// <param name="principal">The principal.</param> /// <param name="principal">The principal.</param>
/// <param name="destinations">The destinations, as a flattened dictionary.</param> /// <param name="destinations">The destinations, as a flattened dictionary.</param>
/// <returns>The principal.</returns> /// <returns>The principal.</returns>
public static ClaimsPrincipal SetDestinations(this ClaimsPrincipal principal, ImmutableDictionary<string, string[]> destinations) public static ClaimsPrincipal SetDestinations(this ClaimsPrincipal principal,
ImmutableDictionary<string, ImmutableArray<string>> destinations)
{ {
if (principal is null) if (principal is null)
{ {

40
src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs

@ -4,9 +4,11 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
@ -141,17 +143,15 @@ public class OpenIddictMessage
continue; continue;
} }
var values = parameter.Select(parameter => parameter.Value).ToArray();
// Note: the core OAuth 2.0 specification requires that request parameters // Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the // not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple // token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter. // 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, [] => default,
1 => values[0], [string value] => new OpenIddictParameter(value),
_ => values [..] values => new(ImmutableCollectionsMarshal.AsImmutableArray(values))
}); });
} }
} }
@ -161,7 +161,7 @@ public class OpenIddictMessage
/// </summary> /// </summary>
/// <param name="parameters">The message parameters.</param> /// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks> /// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, string?[]?>> parameters) public OpenIddictMessage(IEnumerable<KeyValuePair<string, ImmutableArray<string?>?>> parameters)
{ {
if (parameters is null) if (parameters is null)
{ {
@ -180,11 +180,11 @@ public class OpenIddictMessage
// not be present more than once but derived specifications like the // not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple // token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter. // 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, null or [] => default,
1 => parameter.Value[0], [string value] => new OpenIddictParameter(value),
_ => parameter.Value [..] values => new OpenIddictParameter(values)
}); });
} }
} }
@ -213,11 +213,11 @@ public class OpenIddictMessage
// not be present more than once but derived specifications like the // not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple // token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter. // 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, [] => default,
1 => parameter.Value[0], [string value] => new OpenIddictParameter(value),
_ => parameter.Value.ToArray() [..] values => new(ImmutableCollectionsMarshal.AsImmutableArray(values.ToArray()))
}); });
} }
} }
@ -243,17 +243,15 @@ public class OpenIddictMessage
continue; continue;
} }
var values = parameters.GetValues(name);
// Note: the core OAuth 2.0 specification requires that request parameters // Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the // not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple // token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter. // 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, null or [] => default,
1 => values[0], [string value] => new OpenIddictParameter(value),
_ => values [..] values => new(ImmutableCollectionsMarshal.AsImmutableArray<string?>(values))
}); });
} }
} }

314
src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs

@ -8,64 +8,88 @@ using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using Microsoft.Extensions.Primitives;
namespace OpenIddict.Abstractions; namespace OpenIddict.Abstractions;
/// <summary> /// <summary>
/// Represents an OpenIddict parameter value, that can be either a primitive value, /// Represents an OpenIddict parameter value and provides two-way conversion operators that allow
/// an array of strings or a complex JSON representation containing child nodes. /// representing it as a primitive value, an immutable array of strings or a JSON element or node.
/// </summary> /// </summary>
public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter> public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
{ {
private readonly object? _value;
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
public OpenIddictParameter(bool value) => Value = value; public OpenIddictParameter(bool value) => _value = value;
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
public OpenIddictParameter(bool? value) => Value = value; public OpenIddictParameter(bool? value) => _value = value;
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
public OpenIddictParameter(JsonElement value) => Value = value; public OpenIddictParameter(JsonElement value) => _value = value;
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
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
};
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
public OpenIddictParameter(long value) => Value = value; public OpenIddictParameter(long value) => _value = value;
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
public OpenIddictParameter(long? value) => Value = value; public OpenIddictParameter(long? value) => _value = value;
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
public OpenIddictParameter(string? value) => Value = value; public OpenIddictParameter(string? value) => _value = value;
/// <summary> /// <summary>
/// Initializes a new parameter using the specified value. /// Initializes a new parameter using the specified value.
/// </summary> /// </summary>
/// <param name="value">The parameter value.</param> /// <param name="value">The parameter value.</param>
public OpenIddictParameter(string?[]? value) => Value = value; public OpenIddictParameter(ImmutableArray<string?> value)
// Note: to avoid boxing, the underlying array is directly stored as the backing value.
=> _value = ImmutableCollectionsMarshal.AsArray(value);
/// <summary>
/// Initializes a new parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(ImmutableArray<string?>? value) => _value = value switch
{
// Note: to avoid boxing, the underlying array is directly stored as the backing value.
ImmutableArray<string?> array => ImmutableCollectionsMarshal.AsArray(array),
null => null
};
/// <summary> /// <summary>
/// Gets the child item corresponding to the specified index. /// Gets the child item corresponding to the specified index.
@ -89,9 +113,9 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
{ {
get 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, string?[] value => value.Length,
// If the parameter is a JSON array or a JSON object, return its length. // If the parameter is a JSON array or a JSON object, return its length.
@ -135,6 +159,9 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
return element.GetArrayLength(); return element.GetArrayLength();
case JsonValueKind.Object: case JsonValueKind.Object:
#if SUPPORTS_JSON_ELEMENT_PROPERTY_COUNT
return element.GetPropertyCount();
#else
var count = 0; var count = 0;
using (var enumerator = element.EnumerateObject()) using (var enumerator = element.EnumerateObject())
@ -149,6 +176,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
} }
return count; return count;
#endif
default: return 0; default: return 0;
} }
@ -157,11 +185,20 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
} }
/// <summary> /// <summary>
/// Gets the associated value, that can be either a primitive CLR type /// Gets the associated raw value, that can be either a primitive CLR type
/// (e.g bool, string, long), an array of strings or a complex JSON object. /// (e.g bool, string, long), an immutable array of strings or a complex JSON object.
/// </summary> /// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)] [EditorBrowsable(EditorBrowsableState.Never)]
public object? Value { get; } 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
};
/// <summary> /// <summary>
/// Determines whether the current <see cref="OpenIddictParameter"/> /// Determines whether the current <see cref="OpenIddictParameter"/>
@ -174,7 +211,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// </returns> /// </returns>
public bool Equals(OpenIddictParameter other) 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. // If the two parameters reference the same instance, return true.
// //
@ -207,6 +244,9 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
// If the two parameters are JsonElement instances, use the custom comparer. // If the two parameters are JsonElement instances, use the custom comparer.
(JsonElement left, JsonElement right) => DeepEquals(left, right), (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. // When one of the parameters is a JsonElement, compare their underlying values.
(JsonElement { ValueKind: JsonValueKind.True }, bool right) => right, (JsonElement { ValueKind: JsonValueKind.True }, bool right) => right,
(bool left, JsonElement { ValueKind: JsonValueKind.True }) => left, (bool left, JsonElement { ValueKind: JsonValueKind.True }) => left,
@ -252,8 +292,9 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
static bool DeepEquals(JsonElement left, JsonElement right) static bool DeepEquals(JsonElement left, JsonElement right)
{ {
#if !SUPPORTS_JSON_ELEMENT_DEEP_EQUALS
RuntimeHelpers.EnsureSufficientExecutionStack(); RuntimeHelpers.EnsureSufficientExecutionStack();
#endif
switch ((left.ValueKind, right.ValueKind)) switch ((left.ValueKind, right.ValueKind))
{ {
case (JsonValueKind.Undefined, JsonValueKind.Undefined): case (JsonValueKind.Undefined, JsonValueKind.Undefined):
@ -267,6 +308,9 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
case (JsonValueKind.Null, JsonValueKind.Undefined): case (JsonValueKind.Null, JsonValueKind.Undefined):
return true; return true;
#if SUPPORTS_JSON_ELEMENT_DEEP_EQUALS
default: return JsonElement.DeepEquals(left, right);
#else
case (JsonValueKind.Number, JsonValueKind.Number): case (JsonValueKind.Number, JsonValueKind.Number):
return string.Equals(left.GetRawText(), right.GetRawText(), StringComparison.Ordinal); return string.Equals(left.GetRawText(), right.GetRawText(), StringComparison.Ordinal);
@ -312,6 +356,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
} }
default: return false; default: return false;
#endif
} }
} }
} }
@ -333,7 +378,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>The hash code for the current instance.</returns> /// <returns>The hash code for the current instance.</returns>
public override int GetHashCode() public override int GetHashCode()
{ {
return Value switch return _value switch
{ {
// When the parameter value is null, return 0. // When the parameter value is null, return 0.
null => 0, null => 0,
@ -460,7 +505,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>A dictionary of all the parameters associated with the current instance.</returns> /// <returns>A dictionary of all the parameters associated with the current instance.</returns>
public IReadOnlyDictionary<string, OpenIddictParameter> GetNamedParameters() public IReadOnlyDictionary<string, OpenIddictParameter> GetNamedParameters()
{ {
return Value switch return _value switch
{ {
// When the parameter is a JsonElement representing an object, return the requested item. // When the parameter is a JsonElement representing an object, return the requested item.
JsonElement { ValueKind: JsonValueKind.Object } value => GetParametersFromJsonElement(value), JsonElement { ValueKind: JsonValueKind.Object } value => GetParametersFromJsonElement(value),
@ -510,7 +555,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>An enumeration of all the unnamed parameters associated with the current instance.</returns> /// <returns>An enumeration of all the unnamed parameters associated with the current instance.</returns>
public IReadOnlyList<OpenIddictParameter> GetUnnamedParameters() public IReadOnlyList<OpenIddictParameter> GetUnnamedParameters()
{ {
return Value switch return _value switch
{ {
// When the parameter is an array of strings, return its items. // When the parameter is an array of strings, return its items.
string?[] value => GetParametersFromArray(value), string?[] value => GetParametersFromArray(value),
@ -573,7 +618,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// Returns the <see cref="string"/> representation of the current instance. /// Returns the <see cref="string"/> representation of the current instance.
/// </summary> /// </summary>
/// <returns>The <see cref="string"/> representation associated with the parameter value.</returns> /// <returns>The <see cref="string"/> representation associated with the parameter value.</returns>
public override string? ToString() => Value switch public override string? ToString() => _value switch
{ {
null => string.Empty, null => string.Empty,
@ -633,7 +678,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
throw new ArgumentException(SR.GetResourceString(SR.ID0192), nameof(name)); 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. // When the parameter is a JsonElement representing an array, return the requested item.
JsonElement { ValueKind: JsonValueKind.Object } element => JsonElement { ValueKind: JsonValueKind.Object } element =>
@ -669,7 +714,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
throw new ArgumentOutOfRangeException(nameof(index), SR.GetResourceString(SR.ID0193)); 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. // When the parameter is an array of strings, return the requested item.
string?[] array => index < array.Length ? new(array[index]) : null, string?[] array => index < array.Length ? new(array[index]) : null,
@ -706,7 +751,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
throw new ArgumentNullException(nameof(writer)); throw new ArgumentNullException(nameof(writer));
} }
switch (Value) switch (_value)
{ {
// Note: undefined JsonElement values are assimilated to null values. // Note: undefined JsonElement values are assimilated to null values.
case null: case null:
@ -778,7 +823,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public static explicit operator bool?(OpenIddictParameter? parameter) 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. // When the parameter is a null value or a JsonElement representing null, return null.
null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null,
@ -833,7 +878,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public static explicit operator JsonElement(OpenIddictParameter? parameter) 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. // When the parameter is a null value, return an undefined JsonElement.
null => default, null => default,
@ -876,13 +921,14 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public static explicit operator JsonNode?(OpenIddictParameter? parameter) 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. // When the parameter is a null value or a JsonElement representing null, return null.
null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null,
// When the parameter is already a JsonNode, return it as-is. // When the parameter is already a JsonNode, return a clone to ensure mutations
JsonNode value => value, // 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. // When the parameter is a boolean, return a JsonValue.
bool value => JsonValue.Create(value), bool value => JsonValue.Create(value),
@ -972,7 +1018,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public static explicit operator long?(OpenIddictParameter? parameter) 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. // When the parameter is a null value or a JsonElement representing null, return null.
null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null,
@ -1030,7 +1076,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public static explicit operator string?(OpenIddictParameter? parameter) 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. // When the parameter is a null value or a JsonElement representing null, return null.
null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null,
@ -1091,19 +1137,145 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
} }
/// <summary> /// <summary>
/// Converts an <see cref="OpenIddictParameter"/> instance to an array of strings. /// Converts an <see cref="OpenIddictParameter"/> instance to a <see cref="StringValues"/> instance.
/// </summary> /// </summary>
/// <param name="parameter">The parameter to convert.</param> /// <param name="parameter">The parameter to convert.</param>
/// <returns>The converted value.</returns> /// <returns>The converted value.</returns>
public static explicit operator string?[]?(OpenIddictParameter? parameter) public static explicit operator StringValues(OpenIddictParameter? parameter)
=> ((StringValues?) parameter).GetValueOrDefault();
/// <summary>
/// Converts an <see cref="OpenIddictParameter"/> instance to a <see cref="StringValues"/> instance.
/// </summary>
/// <param name="parameter">The parameter to convert.</param>
/// <returns>The converted value.</returns>
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);
}
}
/// <summary>
/// Converts an <see cref="OpenIddictParameter"/> instance to an immutable array of strings.
/// </summary>
/// <param name="parameter">The parameter to convert.</param>
/// <returns>The converted value.</returns>
public static explicit operator ImmutableArray<string?>(OpenIddictParameter? parameter)
=> ((ImmutableArray<string?>?) parameter).GetValueOrDefault();
/// <summary>
/// Converts an <see cref="OpenIddictParameter"/> instance to an immutable array of strings.
/// </summary>
/// <param name="parameter">The parameter to convert.</param>
/// <returns>The converted value.</returns>
public static explicit operator ImmutableArray<string?>?(OpenIddictParameter? parameter)
{
return parameter?._value switch
{ {
// When the parameter is a null value or a JsonElement representing null, return null. // When the parameter is a null value or a JsonElement representing null, return null.
null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null, null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => null,
// When the parameter is already an array of strings, return it as-is. // 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. // When the parameter is a string value, return an array with a single entry.
string value => [value], string value => [value],
@ -1149,7 +1321,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
_ => null _ => null
}; };
static string?[]? ConvertFromJsonElement(JsonElement element) => element.ValueKind switch static ImmutableArray<string?>? ConvertFromJsonElement(JsonElement element) => element.ValueKind switch
{ {
// When the parameter is a JsonElement representing a boolean, // When the parameter is a JsonElement representing a boolean,
// return an 1-item array with its string representation. // return an 1-item array with its string representation.
@ -1166,27 +1338,27 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
_ => null _ => null
}; };
static string?[]? CreateArrayFromJsonElement(JsonElement element) static ImmutableArray<string?>? CreateArrayFromJsonElement(JsonElement element)
{ {
var length = element.GetArrayLength(); var length = element.GetArrayLength();
var array = new string?[length]; var builder = ImmutableArray.CreateBuilder<string?>(length);
for (var index = 0; index < length; index++) for (var index = 0; index < length; index++)
{ {
var item = element[index]; var item = element[index];
if (item.ValueKind is JsonValueKind.True) if (item.ValueKind is JsonValueKind.True)
{ {
array[index] = "true"; builder.Add("true");
} }
else if (item.ValueKind is JsonValueKind.False) else if (item.ValueKind is JsonValueKind.False)
{ {
array[index] = "false"; builder.Add("false");
} }
else if (item.ValueKind is JsonValueKind.String or JsonValueKind.Number) 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. // 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<OpenIddictParameter>
} }
} }
return array; return builder.ToImmutable();
} }
} }
/// <summary> /// <summary>
/// Converts a boolean to an <see cref="OpenIddictParameter"/> instance. /// Converts a boolean to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(bool value) => new(value); public static implicit operator OpenIddictParameter(bool value) => new(value);
/// <summary> /// <summary>
/// Converts a nullable boolean to an <see cref="OpenIddictParameter"/> instance. /// Converts a nullable boolean to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(bool? value) => new(value); public static implicit operator OpenIddictParameter(bool? value) => new(value);
/// <summary> /// <summary>
/// Converts a <see cref="JsonElement"/> to an <see cref="OpenIddictParameter"/> instance. /// Converts a <see cref="JsonElement"/> to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(JsonElement value) => new(value); public static implicit operator OpenIddictParameter(JsonElement value) => new(value);
/// <summary> /// <summary>
/// Converts a <see cref="JsonNode"/> to an <see cref="OpenIddictParameter"/> instance. /// Converts a <see cref="JsonNode"/> to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(JsonNode? value) => new(value); public static implicit operator OpenIddictParameter(JsonNode? value) => new(value);
/// <summary> /// <summary>
/// Converts a long integer to an <see cref="OpenIddictParameter"/> instance. /// Converts a long integer to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(long value) => new(value); public static implicit operator OpenIddictParameter(long value) => new(value);
/// <summary> /// <summary>
/// Converts a nullable long integer to an <see cref="OpenIddictParameter"/> instance. /// Converts a nullable long integer to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(long? value) => new(value); public static implicit operator OpenIddictParameter(long? value) => new(value);
/// <summary> /// <summary>
/// Converts a string to an <see cref="OpenIddictParameter"/> instance. /// Converts a string to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(string? value) => new(value); public static implicit operator OpenIddictParameter(string? value) => new(value);
/// <summary>
/// Converts a string to an <see cref="OpenIddictParameter"/> instance.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
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()))
};
/// <summary>
/// Converts a string to an <see cref="OpenIddictParameter"/> instance.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(StringValues value) => value.Count switch
{
0 => default,
1 => new OpenIddictParameter(value[0]),
_ => new(ImmutableCollectionsMarshal.AsImmutableArray(value.ToArray()))
};
/// <summary>
/// Converts an array of strings to an <see cref="OpenIddictParameter"/> instance.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(ImmutableArray<string?> value) => new(value);
/// <summary> /// <summary>
/// Converts an array of strings to an <see cref="OpenIddictParameter"/> instance. /// Converts an array of strings to an <see cref="OpenIddictParameter"/> instance.
/// </summary> /// </summary>
/// <param name="value">The value to convert</param> /// <param name="value">The value to convert.</param>
/// <returns>An <see cref="OpenIddictParameter"/> instance.</returns> /// <returns>An <see cref="OpenIddictParameter"/> instance.</returns>
public static implicit operator OpenIddictParameter(string?[]? value) => new(value); public static implicit operator OpenIddictParameter(ImmutableArray<string?>? value) => new(value);
/// <summary> /// <summary>
/// Determines whether a parameter is null or empty. /// Determines whether a parameter is null or empty.
@ -1263,7 +1466,7 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
/// <returns><see langword="true"/> if the parameter is null or empty, <see langword="false"/> otherwise.</returns> /// <returns><see langword="true"/> if the parameter is null or empty, <see langword="false"/> otherwise.</returns>
public static bool IsNullOrEmpty(OpenIddictParameter parameter) public static bool IsNullOrEmpty(OpenIddictParameter parameter)
{ {
return parameter.Value switch return parameter._value switch
{ {
null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => true, null or JsonElement { ValueKind: JsonValueKind.Null or JsonValueKind.Undefined } => true,
@ -1298,11 +1501,14 @@ public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
return element.GetArrayLength() is 0; return element.GetArrayLength() is 0;
case JsonValueKind.Object: case JsonValueKind.Object:
#if SUPPORTS_JSON_ELEMENT_PROPERTY_COUNT
return element.GetPropertyCount() is 0;
#else
using (var enumerator = element.EnumerateObject()) using (var enumerator = element.EnumerateObject())
{ {
return !enumerator.MoveNext(); return !enumerator.MoveNext();
} }
#endif
default: return false; default: return false;
} }
} }

19
src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@ -79,7 +80,7 @@ public class OpenIddictRequest : OpenIddictMessage
/// </summary> /// </summary>
/// <param name="parameters">The request parameters.</param> /// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks> /// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, string?[]?>> parameters) public OpenIddictRequest(IEnumerable<KeyValuePair<string, ImmutableArray<string?>?>> parameters)
: base(parameters) : base(parameters)
{ {
} }
@ -134,18 +135,18 @@ public class OpenIddictRequest : OpenIddictMessage
/// <summary> /// <summary>
/// Gets or sets the "audience" parameters. /// Gets or sets the "audience" parameters.
/// </summary> /// </summary>
public string?[]? Audiences public ImmutableArray<string?>? Audiences
{ {
get => (string?[]?) GetParameter(OpenIddictConstants.Parameters.Audience); get => (ImmutableArray<string?>?) GetParameter(OpenIddictConstants.Parameters.Audience);
set => SetParameter(OpenIddictConstants.Parameters.Audience, value); set => SetParameter(OpenIddictConstants.Parameters.Audience, value);
} }
/// <summary> /// <summary>
/// Gets or sets the "claims" parameter. /// Gets or sets the "claims" parameter.
/// </summary> /// </summary>
public JsonElement Claims public JsonObject? Claims
{ {
get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Claims); get => (JsonObject?) GetParameter(OpenIddictConstants.Parameters.Claims);
set => SetParameter(OpenIddictConstants.Parameters.Claims, value); set => SetParameter(OpenIddictConstants.Parameters.Claims, value);
} }
@ -379,9 +380,9 @@ public class OpenIddictRequest : OpenIddictMessage
/// <summary> /// <summary>
/// Gets or sets the "resource" parameters. /// Gets or sets the "resource" parameters.
/// </summary> /// </summary>
public string?[]? Resources public ImmutableArray<string?>? Resources
{ {
get => (string?[]?) GetParameter(OpenIddictConstants.Parameters.Resource); get => (ImmutableArray<string?>?) GetParameter(OpenIddictConstants.Parameters.Resource);
set => SetParameter(OpenIddictConstants.Parameters.Resource, value); set => SetParameter(OpenIddictConstants.Parameters.Resource, value);
} }
@ -442,9 +443,9 @@ public class OpenIddictRequest : OpenIddictMessage
/// <summary> /// <summary>
/// Gets or sets the "registration" parameter. /// Gets or sets the "registration" parameter.
/// </summary> /// </summary>
public JsonElement Registration public JsonObject? Registration
{ {
get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Registration); get => (JsonObject?) GetParameter(OpenIddictConstants.Parameters.Registration);
set => SetParameter(OpenIddictConstants.Parameters.Registration, value); set => SetParameter(OpenIddictConstants.Parameters.Registration, value);
} }

3
src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Text.Json; using System.Text.Json;
@ -78,7 +79,7 @@ public class OpenIddictResponse : OpenIddictMessage
/// </summary> /// </summary>
/// <param name="parameters">The response parameters.</param> /// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks> /// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, string?[]?>> parameters) public OpenIddictResponse(IEnumerable<KeyValuePair<string, ImmutableArray<string?>?>> parameters)
: base(parameters) : base(parameters)
{ {
} }

8
src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Authentication.cs

@ -78,9 +78,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers
#if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS
var location = QueryHelpers.AddQueryString(context.AuthorizationEndpoint, var location = QueryHelpers.AddQueryString(context.AuthorizationEndpoint,
from parameter in context.Request.GetParameters() from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select KeyValuePair.Create(parameter.Key, value)); select KeyValuePair.Create(parameter.Key, value));
#else #else
@ -88,9 +88,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Request.GetParameters() from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

8
src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.Session.cs

@ -78,9 +78,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers
#if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS
var location = QueryHelpers.AddQueryString(context.EndSessionEndpoint, var location = QueryHelpers.AddQueryString(context.EndSessionEndpoint,
from parameter in context.Request.GetParameters() from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select KeyValuePair.Create(parameter.Key, value)); select KeyValuePair.Create(parameter.Key, value));
#else #else
@ -88,9 +88,9 @@ public static partial class OpenIddictClientAspNetCoreHandlers
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Request.GetParameters() from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

37
src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlers.cs

@ -8,6 +8,7 @@ using System.Buffers.Binary;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@ -622,13 +623,17 @@ public static partial class OpenIddictClientAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch context.Parameters[parameter.Key] = parameter.Value switch
{ {
OpenIddictParameter value => value, OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value), JsonElement value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value), JsonNode value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value), bool value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value), int value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value), long value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value), string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
}; };
@ -943,13 +948,17 @@ public static partial class OpenIddictClientAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch context.Parameters[parameter.Key] = parameter.Value switch
{ {
OpenIddictParameter value => value, OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value), JsonElement value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value), JsonNode value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value), bool value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value), int value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value), long value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value), string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
}; };

2
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. // Read the version of the format used to serialize the ticket.
var version = reader.ReadInt32(); var version = reader.ReadInt32();
if (version != 5) if (version is not 5)
{ {
throw new InvalidOperationException(SR.GetResourceString(SR.ID0287)); throw new InvalidOperationException(SR.GetResourceString(SR.ID0287));
} }

4
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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Request.GetParameters() from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

4
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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Request.GetParameters() from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

11
src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs

@ -14,6 +14,7 @@ using System.Text;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using OpenIddict.Extensions; using OpenIddict.Extensions;
using Owin; using Owin;
@ -265,7 +266,10 @@ public static partial class OpenIddictClientOwinHandlers
if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase)) 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<string, StringValues>(parameter.Key, values));
} }
else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase))
@ -296,7 +300,10 @@ public static partial class OpenIddictClientOwinHandlers
return; 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<string, StringValues>(parameter.Key, values));
} }
else else

5
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs

@ -6,6 +6,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -78,7 +79,9 @@ public sealed class OpenIddictClientSystemIntegrationActivationHandler : IHosted
} }
#endif #endif
// Otherwise, try to extract the protocol activation from the command line arguments. // 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); return new OpenIddictClientSystemIntegrationActivation(value);
} }

44
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs

@ -240,8 +240,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
NSUrl CreateUrl() => new(OpenIddictHelpers.AddQueryStringParameters( NSUrl CreateUrl() => new(OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri); static parameter => (StringValues) parameter.Value)).AbsoluteUri);
void HandleCallback(NSUrl? url, NSError? error) void HandleCallback(NSUrl? url, NSError? error)
{ {
@ -253,12 +253,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query)) foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -270,12 +265,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment)) foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -370,8 +360,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
intent.LaunchUrl(Application.Context, NativeUri.Parse(OpenIddictHelpers.AddQueryStringParameters( intent.LaunchUrl(Application.Context, NativeUri.Parse(OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri)!); static parameter => (StringValues) parameter.Value)).AbsoluteUri)!);
context.HandleRequest(); context.HandleRequest();
#else #else
@ -455,8 +445,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
requestUri : OpenIddictHelpers.AddQueryStringParameters( requestUri : OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))), static parameter => (StringValues) parameter.Value)),
callbackUri: new Uri(context.RedirectUri, UriKind.Absolute))) callbackUri: new Uri(context.RedirectUri, UriKind.Absolute)))
{ {
case { ResponseStatus: WebAuthenticationStatus.Success } result case { ResponseStatus: WebAuthenticationStatus.Success } result
@ -467,12 +457,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -484,12 +469,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -576,8 +556,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
var uri = OpenIddictHelpers.AddQueryStringParameters( var uri = OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute), uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))); static parameter => (StringValues) parameter.Value));
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {

44
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs

@ -240,8 +240,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
NSUrl CreateUrl() => new(OpenIddictHelpers.AddQueryStringParameters( NSUrl CreateUrl() => new(OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri); static parameter => (StringValues) parameter.Value)).AbsoluteUri);
void HandleCallback(NSUrl? url, NSError? error) void HandleCallback(NSUrl? url, NSError? error)
{ {
@ -253,12 +253,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query)) foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -270,12 +265,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment)) foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -370,8 +360,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
intent.LaunchUrl(Application.Context, NativeUri.Parse(OpenIddictHelpers.AddQueryStringParameters( intent.LaunchUrl(Application.Context, NativeUri.Parse(OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))).AbsoluteUri)!); static parameter => (StringValues) parameter.Value)).AbsoluteUri)!);
context.HandleRequest(); context.HandleRequest();
#else #else
@ -455,8 +445,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
requestUri : OpenIddictHelpers.AddQueryStringParameters( requestUri : OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))), static parameter => (StringValues) parameter.Value)),
callbackUri: new Uri(context.PostLogoutRedirectUri, UriKind.Absolute))) callbackUri: new Uri(context.PostLogoutRedirectUri, UriKind.Absolute)))
{ {
case { ResponseStatus: WebAuthenticationStatus.Success } result case { ResponseStatus: WebAuthenticationStatus.Success } result
@ -467,12 +457,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -484,12 +469,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
{ {
foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -576,8 +556,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
var uri = OpenIddictHelpers.AddQueryStringParameters( var uri = OpenIddictHelpers.AddQueryStringParameters(
uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute), uri: new Uri(context.EndSessionEndpoint, UriKind.Absolute),
parameters: context.Transaction.Request.GetParameters().ToDictionary( parameters: context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))); static parameter => (StringValues) parameter.Value));
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {

3
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
@ -378,7 +379,7 @@ public static class OpenIddictClientSystemIntegrationHelpers
/// The <see cref="Uri"/> if the application instance was activated /// The <see cref="Uri"/> if the application instance was activated
/// via a protocol activation, <see langword="null"/> otherwise. /// via a protocol activation, <see langword="null"/> otherwise.
/// </returns> /// </returns>
internal static Uri? GetProtocolActivationUriFromCommandLineArguments(string?[]? arguments) => arguments switch internal static Uri? GetProtocolActivationUriFromCommandLineArguments(ImmutableArray<string> arguments) => arguments switch
{ {
// In most cases, the first segment present in the command line arguments contains the path of the // 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 // executable, but it's technically possible to start an application in a way that the command line

14
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs

@ -69,12 +69,7 @@ public sealed class OpenIddictClientSystemIntegrationService
{ {
foreach (var parameter in OpenIddictHelpers.ParseQuery(intent.Data.Query)) foreach (var parameter in OpenIddictHelpers.ParseQuery(intent.Data.Query))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }
@ -86,12 +81,7 @@ public sealed class OpenIddictClientSystemIntegrationService
{ {
foreach (var parameter in OpenIddictHelpers.ParseFragment(intent.Data.Fragment)) foreach (var parameter in OpenIddictHelpers.ParseFragment(intent.Data.Fragment))
{ {
parameters[parameter.Key] = parameter.Value.Count switch parameters[parameter.Key] = parameter.Value;
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
} }
} }

8
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs

@ -1044,8 +1044,8 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
{ {
request.RequestUri = OpenIddictHelpers.AddQueryStringParameters(request.RequestUri, request.RequestUri = OpenIddictHelpers.AddQueryStringParameters(request.RequestUri,
context.Transaction.Request.GetParameters().ToDictionary( context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))); static parameter => (StringValues) parameter.Value));
} }
// For POST requests, attach the request parameters to the request form by default. // 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( request.Content = new FormUrlEncodedContent(
from parameter in context.Transaction.Request.GetParameters() from parameter in context.Transaction.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
select new KeyValuePair<string?, string?>(parameter.Key, value)); select new KeyValuePair<string?, string?>(parameter.Key, value));
} }

16
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: // The following parameters MUST be formatted as arrays of objects:
JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element && 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. // Parameters that are not in the well-known list can be of any type.
_ => true _ => true
}; };
static bool ValidateObjectArray(JsonElement element)
{
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind is not JsonValueKind.Object)
{
return false;
}
}
return true;
}
} }
} }

24
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs

@ -102,9 +102,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
#if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS
var location = QueryHelpers.AddQueryString(context.RequestUri.GetLeftPart(UriPartial.Path), var location = QueryHelpers.AddQueryString(context.RequestUri.GetLeftPart(UriPartial.Path),
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select KeyValuePair.Create(parameter.Key, value)); select KeyValuePair.Create(parameter.Key, value));
#else #else
@ -112,9 +112,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: 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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {
@ -264,9 +264,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
#if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS
var location = QueryHelpers.AddQueryString(context.RedirectUri, var location = QueryHelpers.AddQueryString(context.RedirectUri,
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select KeyValuePair.Create(parameter.Key, value)); select KeyValuePair.Create(parameter.Key, value));
#else #else
@ -274,9 +274,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: 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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

16
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs

@ -84,9 +84,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
#if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS
var location = QueryHelpers.AddQueryString(context.RequestUri.GetLeftPart(UriPartial.Path), var location = QueryHelpers.AddQueryString(context.RequestUri.GetLeftPart(UriPartial.Path),
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select KeyValuePair.Create(parameter.Key, value)); select KeyValuePair.Create(parameter.Key, value));
#else #else
@ -94,9 +94,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {
@ -154,9 +154,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
#if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS #if SUPPORTS_MULTIPLE_VALUES_IN_QUERYHELPERS
var location = QueryHelpers.AddQueryString(context.PostLogoutRedirectUri, var location = QueryHelpers.AddQueryString(context.PostLogoutRedirectUri,
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select KeyValuePair.Create(parameter.Key, value)); select KeyValuePair.Create(parameter.Key, value));
#else #else
@ -164,9 +164,9 @@ public static partial class OpenIddictServerAspNetCoreHandlers
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

55
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs

@ -7,6 +7,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
@ -257,13 +258,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch context.Parameters[parameter.Key] = parameter.Value switch
{ {
OpenIddictParameter value => value, OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value), JsonElement value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value), JsonNode value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value), bool value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value), int value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value), long value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value), string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
}; };
@ -354,13 +359,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch context.Parameters[parameter.Key] = parameter.Value switch
{ {
OpenIddictParameter value => value, OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value), JsonElement value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value), JsonNode value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value), bool value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value), int value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value), long value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value), string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
}; };
@ -413,13 +422,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch context.Parameters[parameter.Key] = parameter.Value switch
{ {
OpenIddictParameter value => value, OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value), JsonElement value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value), JsonNode value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value), bool value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value), int value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value), long value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value), string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
}; };

2
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. // Read the version of the format used to serialize the ticket.
var version = reader.ReadInt32(); var version = reader.ReadInt32();
if (version != 5) if (version is not 5)
{ {
throw new InvalidOperationException(SR.GetResourceString(SR.ID0287)); throw new InvalidOperationException(SR.GetResourceString(SR.ID0287));
} }

16
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs

@ -104,9 +104,9 @@ public static partial class OpenIddictServerOwinHandlers
var location = context.RequestUri.GetLeftPart(UriPartial.Path); var location = context.RequestUri.GetLeftPart(UriPartial.Path);
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: 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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: 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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: 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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

8
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs

@ -84,9 +84,9 @@ public static partial class OpenIddictServerOwinHandlers
var location = context.RequestUri.GetLeftPart(UriPartial.Path); var location = context.RequestUri.GetLeftPart(UriPartial.Path);
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: 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. // For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in foreach (var (key, value) in
from parameter in context.Response.GetParameters() from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value) where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value)) select (parameter.Key, Value: value))
{ {

21
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs

@ -13,6 +13,7 @@ using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using OpenIddict.Extensions; using OpenIddict.Extensions;
using Owin; using Owin;
using static OpenIddict.Server.Owin.OpenIddictServerOwinConstants; using static OpenIddict.Server.Owin.OpenIddictServerOwinConstants;
@ -524,7 +525,10 @@ public static partial class OpenIddictServerOwinHandlers
if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase)) 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<string, StringValues>(parameter.Key, values));
} }
else else
@ -575,7 +579,10 @@ public static partial class OpenIddictServerOwinHandlers
if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase)) 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<string, StringValues>(parameter.Key, values));
} }
else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase))
@ -606,7 +613,10 @@ public static partial class OpenIddictServerOwinHandlers
return; 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<string, StringValues>(parameter.Key, values));
} }
else else
@ -681,7 +691,10 @@ public static partial class OpenIddictServerOwinHandlers
return; 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<string, StringValues>(parameter.Key, values));
} }
else else

3
src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs

@ -6,6 +6,7 @@
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes;
namespace OpenIddict.Server; namespace OpenIddict.Server;
@ -102,7 +103,7 @@ public static partial class OpenIddictServerEvents
/// Note: this value should only be populated if the "address" /// Note: this value should only be populated if the "address"
/// scope was requested and accepted by the resource owner. /// scope was requested and accepted by the resource owner.
/// </summary> /// </summary>
public JsonElement Address { get; set; } public JsonObject? Address { get; set; }
/// <summary> /// <summary>
/// Gets or sets the values used for the "aud" claim. /// Gets or sets the values used for the "aud" claim.

28
src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs

@ -246,20 +246,20 @@ public static partial class OpenIddictServerHandlers
[Metadata.DeviceAuthorizationEndpoint] = notification.DeviceAuthorizationEndpoint?.AbsoluteUri, [Metadata.DeviceAuthorizationEndpoint] = notification.DeviceAuthorizationEndpoint?.AbsoluteUri,
[Metadata.PushedAuthorizationRequestEndpoint] = notification.PushedAuthorizationEndpoint?.AbsoluteUri, [Metadata.PushedAuthorizationRequestEndpoint] = notification.PushedAuthorizationEndpoint?.AbsoluteUri,
[Metadata.JwksUri] = notification.JsonWebKeySetEndpoint?.AbsoluteUri, [Metadata.JwksUri] = notification.JsonWebKeySetEndpoint?.AbsoluteUri,
[Metadata.GrantTypesSupported] = notification.GrantTypes.ToArray(), [Metadata.GrantTypesSupported] = notification.GrantTypes.ToImmutableArray<string?>(),
[Metadata.ResponseTypesSupported] = notification.ResponseTypes.ToArray(), [Metadata.ResponseTypesSupported] = notification.ResponseTypes.ToImmutableArray<string?>(),
[Metadata.ResponseModesSupported] = notification.ResponseModes.ToArray(), [Metadata.ResponseModesSupported] = notification.ResponseModes.ToImmutableArray<string?>(),
[Metadata.ScopesSupported] = notification.Scopes.ToArray(), [Metadata.ScopesSupported] = notification.Scopes.ToImmutableArray<string?>(),
[Metadata.ClaimsSupported] = notification.Claims.ToArray(), [Metadata.ClaimsSupported] = notification.Claims.ToImmutableArray<string?>(),
[Metadata.IdTokenSigningAlgValuesSupported] = notification.IdTokenSigningAlgorithms.ToArray(), [Metadata.IdTokenSigningAlgValuesSupported] = notification.IdTokenSigningAlgorithms.ToImmutableArray<string?>(),
[Metadata.CodeChallengeMethodsSupported] = notification.CodeChallengeMethods.ToArray(), [Metadata.CodeChallengeMethodsSupported] = notification.CodeChallengeMethods.ToImmutableArray<string?>(),
[Metadata.SubjectTypesSupported] = notification.SubjectTypes.ToArray(), [Metadata.SubjectTypesSupported] = notification.SubjectTypes.ToImmutableArray<string?>(),
[Metadata.PromptValuesSupported] = notification.PromptValues.ToArray(), [Metadata.PromptValuesSupported] = notification.PromptValues.ToImmutableArray<string?>(),
[Metadata.TokenEndpointAuthMethodsSupported] = notification.TokenEndpointAuthenticationMethods.ToArray(), [Metadata.TokenEndpointAuthMethodsSupported] = notification.TokenEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.IntrospectionEndpointAuthMethodsSupported] = notification.IntrospectionEndpointAuthenticationMethods.ToArray(), [Metadata.IntrospectionEndpointAuthMethodsSupported] = notification.IntrospectionEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.RevocationEndpointAuthMethodsSupported] = notification.RevocationEndpointAuthenticationMethods.ToArray(), [Metadata.RevocationEndpointAuthMethodsSupported] = notification.RevocationEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.DeviceAuthorizationEndpointAuthMethodsSupported] = notification.DeviceAuthorizationEndpointAuthenticationMethods.ToArray(), [Metadata.DeviceAuthorizationEndpointAuthMethodsSupported] = notification.DeviceAuthorizationEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported] = notification.PushedAuthorizationEndpointAuthenticationMethods.ToArray(), [Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported] = notification.PushedAuthorizationEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.RequirePushedAuthorizationRequests] = notification.RequirePushedAuthorizationRequests [Metadata.RequirePushedAuthorizationRequests] = notification.RequirePushedAuthorizationRequests
}; };

2
src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs

@ -268,7 +268,7 @@ public static partial class OpenIddictServerHandlers
break; break;
default: default:
response[Claims.Audience] = notification.Audiences.ToArray(); response[Claims.Audience] = notification.Audiences.ToImmutableArray<string?>();
break; break;
} }

13
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). // 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 // Note: starting in 7.0, Wilson no longer uses JSON.NET and supports a limited set of types for the
// TryGetPayloadValue() API. Since ImmutableDictionary<string, string[]> is not supported, the value // TryGetPayloadValue() API. Since ImmutableDictionary<string, string[]> is not supported, the value is
// is retrieved as a Dictionary<string, string[]>, which is supported by both Wilson 6.x and 7.x. // retrieved as a Dictionary<string, string[]> and converted to ImmutableDictionary<string, ImmutableArray<string>.
if (token.TryGetPayloadValue(Claims.Private.ClaimDestinationsMap, out Dictionary<string, string[]> destinations)) if (token.TryGetPayloadValue(Claims.Private.ClaimDestinationsMap, out Dictionary<string, string[]> destinations))
{ {
context.Principal.SetDestinations(destinations.ToImmutableDictionary()); var builder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>();
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); context.Logger.LogTrace(SR.GetResourceString(SR.ID6001), context.Token, context.Principal.Claims);

2
src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs

@ -243,7 +243,7 @@ public static partial class OpenIddictServerHandlers
break; break;
default: default:
response[Claims.Audience] = notification.Audiences.ToArray(); response[Claims.Audience] = notification.Audiences.ToImmutableArray<string?>();
break; break;
} }

19
src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs

@ -7,6 +7,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
@ -368,13 +369,17 @@ public static partial class OpenIddictValidationAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch context.Parameters[parameter.Key] = parameter.Value switch
{ {
OpenIddictParameter value => value, OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value), JsonElement value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value), JsonNode value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value), bool value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value), int value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value), long value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value), string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
}; };

2
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. // Read the version of the format used to serialize the ticket.
var version = reader.ReadInt32(); var version = reader.ReadInt32();
if (version != 5) if (version is not 5)
{ {
throw new InvalidOperationException(SR.GetResourceString(SR.ID0287)); throw new InvalidOperationException(SR.GetResourceString(SR.ID0287));
} }

8
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs

@ -568,8 +568,8 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
{ {
request.RequestUri = OpenIddictHelpers.AddQueryStringParameters(request.RequestUri, request.RequestUri = OpenIddictHelpers.AddQueryStringParameters(request.RequestUri,
context.Transaction.Request.GetParameters().ToDictionary( context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))); static parameter => (StringValues) parameter.Value));
} }
// For POST requests, attach the request parameters to the request form by default. // 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( request.Content = new FormUrlEncodedContent(
from parameter in context.Transaction.Request.GetParameters() from parameter in context.Transaction.Request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
select new KeyValuePair<string?, string?>(parameter.Key, value)); select new KeyValuePair<string?, string?>(parameter.Key, value));
} }

16
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: // The following parameters MUST be formatted as arrays of objects:
JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element && 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. // Parameters that are not in the well-known list can be of any type.
_ => true _ => true
}; };
static bool ValidateObjectArray(JsonElement element)
{
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind is not JsonValueKind.Object)
{
return false;
}
}
return true;
}
} }
} }

2
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs

@ -269,7 +269,7 @@ public class OpenIddictConverterTests
var message = new OpenIddictMessage(); var message = new OpenIddictMessage();
message.AddParameter("string", "value"); message.AddParameter("string", "value");
message.AddParameter("array", new[] { "value" }); message.AddParameter("array", new OpenIddictParameter(["value"]));
// Act // Act
converter.Write(writer, value: message, options: null!); converter.Write(writer, value: message, options: null!);

16
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs

@ -1125,8 +1125,8 @@ public class OpenIddictExtensionsTests
// Assert // Assert
Assert.Equal(2, destinations.Count); Assert.Equal(2, destinations.Count);
Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], destinations[Claims.Name]); Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], [.. destinations[Claims.Name]]);
Assert.Equal<string[]>([Destinations.IdentityToken], destinations[Claims.Email]); Assert.Equal<string[]>([Destinations.IdentityToken], [.. destinations[Claims.Email]]);
} }
[Fact] [Fact]
@ -1159,8 +1159,8 @@ public class OpenIddictExtensionsTests
// Assert // Assert
Assert.Equal(2, destinations.Count); Assert.Equal(2, destinations.Count);
Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], destinations[Claims.Name]); Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], [.. destinations[Claims.Name]]);
Assert.Equal<string[]>([Destinations.IdentityToken], destinations[Claims.Email]); Assert.Equal<string[]>([Destinations.IdentityToken], [.. destinations[Claims.Email]]);
} }
[Fact] [Fact]
@ -1192,7 +1192,7 @@ public class OpenIddictExtensionsTests
{ {
// Arrange // Arrange
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
var destinations = (ImmutableDictionary<string, string[]>) null!; var destinations = (ImmutableDictionary<string, ImmutableArray<string>>) null!;
// Act and assert // Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => identity.SetDestinations(destinations)); var exception = Assert.Throws<ArgumentNullException>(() => identity.SetDestinations(destinations));
@ -1205,7 +1205,7 @@ public class OpenIddictExtensionsTests
{ {
// Arrange // Arrange
var principal = new ClaimsPrincipal(new ClaimsIdentity()); var principal = new ClaimsPrincipal(new ClaimsIdentity());
var destinations = (ImmutableDictionary<string, string[]>) null!; var destinations = (ImmutableDictionary<string, ImmutableArray<string>>) null!;
// Act and assert // Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => principal.SetDestinations(destinations)); var exception = Assert.Throws<ArgumentNullException>(() => principal.SetDestinations(destinations));
@ -1226,7 +1226,7 @@ public class OpenIddictExtensionsTests
var identity = new ClaimsIdentity(claims); var identity = new ClaimsIdentity(claims);
var destinations = ImmutableDictionary.CreateBuilder<string, string[]>(StringComparer.Ordinal); var destinations = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(StringComparer.Ordinal);
destinations.Add(Claims.Name, [Destinations.AccessToken, Destinations.IdentityToken]); destinations.Add(Claims.Name, [Destinations.AccessToken, Destinations.IdentityToken]);
destinations.Add(Claims.Email, [Destinations.IdentityToken]); destinations.Add(Claims.Email, [Destinations.IdentityToken]);
destinations.Add(Claims.Nonce, []); destinations.Add(Claims.Nonce, []);
@ -1253,7 +1253,7 @@ public class OpenIddictExtensionsTests
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims)); var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));
var destinations = ImmutableDictionary.CreateBuilder<string, string[]>(StringComparer.Ordinal); var destinations = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(StringComparer.Ordinal);
destinations.Add(Claims.Name, [Destinations.AccessToken, Destinations.IdentityToken]); destinations.Add(Claims.Name, [Destinations.AccessToken, Destinations.IdentityToken]);
destinations.Add(Claims.Email, [Destinations.IdentityToken]); destinations.Add(Claims.Email, [Destinations.IdentityToken]);
destinations.Add(Claims.Nonce, []); destinations.Add(Claims.Nonce, []);

19
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
@ -98,7 +99,8 @@ public class OpenIddictMessageTests
// Assert // Assert
Assert.Equal(1, message.Count); Assert.Equal(1, message.Count);
Assert.Equal<string?[]?>(["Fabrikam", "Contoso"], (string?[]?) message.GetParameter("parameter")); Assert.Equal<IEnumerable<string?>?>(["Fabrikam", "Contoso"],
(ImmutableArray<string?>?) message.GetParameter("parameter"));
} }
[Fact] [Fact]
@ -107,12 +109,13 @@ public class OpenIddictMessageTests
// Arrange and act // Arrange and act
var message = new OpenIddictMessage( var message = new OpenIddictMessage(
[ [
new KeyValuePair<string, string?[]?>("parameter", ["Fabrikam", "Contoso"]) new KeyValuePair<string, ImmutableArray<string?>?>("parameter", ["Fabrikam", "Contoso"])
]); ]);
// Assert // Assert
Assert.Equal(1, message.Count); Assert.Equal(1, message.Count);
Assert.Equal<string?[]?>(["Fabrikam", "Contoso"], (string?[]?) message.GetParameter("parameter")); Assert.Equal<IEnumerable<string?>?>(["Fabrikam", "Contoso"],
(ImmutableArray<string?>?) message.GetParameter("parameter"));
} }
[Fact] [Fact]
@ -121,12 +124,12 @@ public class OpenIddictMessageTests
// Arrange and act // Arrange and act
var message = new OpenIddictMessage( var message = new OpenIddictMessage(
[ [
new KeyValuePair<string, string?[]?>("parameter", ["Fabrikam"]) new KeyValuePair<string, ImmutableArray<string?>?>("parameter", ["Fabrikam"])
]); ]);
// Assert // Assert
Assert.Equal(1, message.Count); Assert.Equal(1, message.Count);
Assert.Equal("Fabrikam", message.GetParameter("parameter")?.Value); Assert.Equal("Fabrikam", (string?) message.GetParameter("parameter"));
} }
[Theory] [Theory]
@ -423,7 +426,7 @@ public class OpenIddictMessageTests
// Act and assert // Act and assert
Assert.True(message.TryGetParameter("parameter", out var parameter)); Assert.True(message.TryGetParameter("parameter", out var parameter));
Assert.Equal(42, (long?) parameter.Value); Assert.Equal(42, (long?) parameter);
} }
[Fact] [Fact]
@ -434,7 +437,7 @@ public class OpenIddictMessageTests
// Act and assert // Act and assert
Assert.False(message.TryGetParameter("parameter", out OpenIddictParameter parameter)); Assert.False(message.TryGetParameter("parameter", out OpenIddictParameter parameter));
Assert.Null(parameter.Value); Assert.Equal(default, parameter);
} }
[Fact] [Fact]
@ -507,7 +510,7 @@ public class OpenIddictMessageTests
// Arrange // Arrange
var message = new OpenIddictMessage var message = new OpenIddictMessage
{ {
["redirect_uris"] = new[] { "https://abc.org/callback" }, ["redirect_uris"] = new OpenIddictParameter(["https://abc.org/callback"]),
["client_name"] = "My Example Client" ["client_name"] = "My Example Client"
}; };

231
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs

@ -4,9 +4,12 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using Microsoft.Extensions.Primitives;
using Xunit; using Xunit;
namespace OpenIddict.Abstractions.Tests.Primitives; namespace OpenIddict.Abstractions.Tests.Primitives;
@ -191,9 +194,9 @@ public class OpenIddictParameterTests
var parameter = new OpenIddictParameter(["Fabrikam", "Contoso"]); var parameter = new OpenIddictParameter(["Fabrikam", "Contoso"]);
// Act and assert // Act and assert
Assert.True(parameter.Equals(new string[] { "Fabrikam", "Contoso" })); Assert.True(parameter.Equals(new OpenIddictParameter(["Fabrikam", "Contoso"])));
Assert.False(parameter.Equals(new string[] { "Fabrikam" })); Assert.False(parameter.Equals(new OpenIddictParameter(["Fabrikam"])));
Assert.False(parameter.Equals(new string[] { "Contoso", "Fabrikam" })); Assert.False(parameter.Equals(new OpenIddictParameter(["Contoso", "Fabrikam"])));
} }
[Fact] [Fact]
@ -203,11 +206,11 @@ public class OpenIddictParameterTests
var parameter = new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]")); var parameter = new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"));
// Act and assert // Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"))); Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("{}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("{}"))));
Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3)))); Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3))));
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonArray()))); 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)); var parameter = new OpenIddictParameter(new JsonArray(0, 1, 2, 3));
// Act and assert // Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"))); Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("{}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("{}"))));
Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3)))); Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3))));
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonArray()))); Assert.False(parameter.Equals(new OpenIddictParameter(new JsonArray())));
@ -243,11 +246,11 @@ public class OpenIddictParameterTests
var parameter = new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}")); var parameter = new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"));
// Act and assert // Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"))); Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"[]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"[]"))));
Assert.True(parameter.Equals(new OpenIddictParameter(new JsonObject Assert.True(parameter.Equals(new OpenIddictParameter(new JsonObject
{ {
@ -285,23 +288,23 @@ public class OpenIddictParameterTests
}); });
// Act and assert // Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"))); Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}"))));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"[]"))); Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"[]"))));
Assert.True(parameter.Equals(new JsonObject Assert.True(parameter.Equals(new OpenIddictParameter(new JsonObject
{ {
["field"] = new JsonArray(0, 1, 2, 3) ["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") ["field"] = JsonValue.Create("value")
})); })));
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject
{ {
@ -309,20 +312,20 @@ public class OpenIddictParameterTests
@"{""field"":""value""}").GetProperty("field")) @"{""field"":""value""}").GetProperty("field"))
}))); })));
Assert.False(parameter.Equals(new JsonObject Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject
{ {
["field"] = new JsonArray(0, 1, 2) ["field"] = new JsonArray(0, 1, 2)
})); })));
Assert.True(parameter.Equals(JsonValue.Create(new Dictionary<string, object> Assert.True(parameter.Equals(new OpenIddictParameter(JsonValue.Create(new Dictionary<string, object>
{ {
["field"] = new JsonArray(0, 1, 2, 3) ["field"] = new JsonArray(0, 1, 2, 3)
}))); }))));
Assert.True(parameter.Equals(JsonValue.Create(new Dictionary<string, object> Assert.True(parameter.Equals(new OpenIddictParameter(JsonValue.Create(new Dictionary<string, object>
{ {
["field"] = new[] { 0, 1, 2, 3 } ["field"] = new[] { 0, 1, 2, 3 }
}))); }))));
} }
[Fact] [Fact]
@ -797,11 +800,7 @@ public class OpenIddictParameterTests
public void GetNamedParameters_ReturnsEmptyDictionaryForArrays() public void GetNamedParameters_ReturnsEmptyDictionaryForArrays()
{ {
// Arrange // Arrange
var parameters = new[] ImmutableArray<string?> parameters = ["Fabrikam", "Contoso"];
{
"Fabrikam",
"Contoso"
};
var parameter = new OpenIddictParameter(parameters); var parameter = new OpenIddictParameter(parameters);
@ -924,11 +923,7 @@ public class OpenIddictParameterTests
public void GetUnnamedParameters_ReturnsExpectedParametersForArrays() public void GetUnnamedParameters_ReturnsExpectedParametersForArrays()
{ {
// Arrange // Arrange
var parameters = new[] ImmutableArray<string?> parameters = ["Fabrikam", "Contoso"];
{
"Fabrikam",
"Contoso"
};
var parameter = new OpenIddictParameter(parameters); 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((bool?) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((long?) 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((string?[]?) null))); Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((ImmutableArray<string?>?) null)));
} }
[Fact] [Fact]
@ -1502,11 +1497,11 @@ public class OpenIddictParameterTests
public void BoolConverter_CanCreateParameterFromBooleanValue() public void BoolConverter_CanCreateParameterFromBooleanValue()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.True((bool?) new OpenIddictParameter(true).Value); Assert.True((bool?) new OpenIddictParameter(true));
Assert.True((bool?) new OpenIddictParameter((bool?) true).Value); Assert.True((bool?) new OpenIddictParameter((bool?) true));
Assert.False((bool?) new OpenIddictParameter(false).Value); Assert.False((bool?) new OpenIddictParameter(false));
Assert.False((bool?) new OpenIddictParameter((bool?) false).Value); Assert.False((bool?) new OpenIddictParameter((bool?) false));
} }
[Fact] [Fact]
@ -1898,8 +1893,8 @@ public class OpenIddictParameterTests
public void LongConverter_CanCreateParameterFromLongValue() public void LongConverter_CanCreateParameterFromLongValue()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.Equal(42, (long?) new OpenIddictParameter(42).Value); Assert.Equal(42, (long?) new OpenIddictParameter(42));
Assert.Equal(42, (long?) new OpenIddictParameter((long?) 42).Value); Assert.Equal(42, (long?) new OpenIddictParameter((long?) 42));
} }
[Fact] [Fact]
@ -1970,7 +1965,7 @@ public class OpenIddictParameterTests
public void StringConverter_CanCreateParameterFromStringValue() public void StringConverter_CanCreateParameterFromStringValue()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.Equal("Fabrikam", (string?) new OpenIddictParameter("Fabrikam").Value); Assert.Equal("Fabrikam", (string?) new OpenIddictParameter("Fabrikam"));
} }
[Fact] [Fact]
@ -2041,77 +2036,155 @@ public class OpenIddictParameterTests
public void StringArrayConverter_CanCreateParameterFromArray() public void StringArrayConverter_CanCreateParameterFromArray()
{ {
// Arrange // Arrange
var array = new[] { "Fabrikam", "Contoso" }; string?[] array = ["Fabrikam", "Contoso"];
// Act // Act
var parameter = new OpenIddictParameter(array); var parameter = new OpenIddictParameter([.. array]);
// Assert // Assert
Assert.Same(array, parameter.Value); Assert.Equal<IEnumerable<string?>?>(array, (ImmutableArray<string?>?) parameter);
} }
[Fact] [Fact]
public void StringArrayConverter_CanCreateParameterFromPrimitiveValues() public void StringArrayConverter_CanCreateParameterFromPrimitiveValues()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter("Fabrikam")); Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter("Fabrikam"));
Assert.Equal<string?[]?>(["false"], (string?[]?) new OpenIddictParameter(false)); Assert.Equal<IEnumerable<string?>?>(["false"], (ImmutableArray<string?>?) new OpenIddictParameter(false));
Assert.Equal<string?[]?>(["42"], (string?[]?) new OpenIddictParameter(42)); Assert.Equal<IEnumerable<string?>?>(["42"], (ImmutableArray<string?>?) new OpenIddictParameter(42));
} }
[Fact] [Fact]
public void StringArrayConverter_ReturnsDefaultValueForNullValues() public void StringArrayConverter_ReturnsDefaultValueForNullValues()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.Null((string?[]?) new OpenIddictParameter()); Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter());
} }
[Fact] [Fact]
public void StringArrayConverter_ReturnsSingleElementArrayForStringValue() public void StringArrayConverter_ReturnsSingleElementArrayForStringValue()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter("Fabrikam")); Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter("Fabrikam"));
} }
[Fact] [Fact]
public void StringArrayConverter_ReturnsDefaultValueForUnsupportedJsonValues() public void StringArrayConverter_ReturnsDefaultValueForUnsupportedJsonValues()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.Null((string?[]?) new OpenIddictParameter(new JsonElement())); Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(new JsonElement()));
Assert.Null((string?[]?) new OpenIddictParameter( Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""value"",[]]"))); JsonSerializer.Deserialize<JsonElement>(@"[""value"",[]]")));
Assert.Null((string?[]?) new OpenIddictParameter( Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""value"",{}]"))); JsonSerializer.Deserialize<JsonElement>(@"[""value"",{}]")));
Assert.Null((string?[]?) new OpenIddictParameter((JsonNode?) null)); Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter((JsonNode?) null));
Assert.Null((string?[]?) new OpenIddictParameter(new JsonArray("value", new JsonArray()))); Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(new JsonArray("value", new JsonArray())));
Assert.Null((string?[]?) new OpenIddictParameter(new JsonArray("value", new JsonObject()))); Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(new JsonArray("value", new JsonObject())));
} }
[Fact] [Fact]
public void StringArrayConverter_CanConvertFromJsonValues() public void StringArrayConverter_CanConvertFromJsonValues()
{ {
// Arrange, act and assert // Arrange, act and assert
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter( Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"{""field"":""Fabrikam""}").GetProperty("field")));
Assert.Equal<IEnumerable<string?>?>(["false"], (ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"{""field"":false}").GetProperty("field")));
Assert.Equal<IEnumerable<string?>?>(["42"], (ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"{""field"":42}").GetProperty("field")));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""Fabrikam""]")));
Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""Contoso"",""Fabrikam""]")));
Assert.Equal<IEnumerable<string?>?>(["value", "42", "true"], (ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""value"",42,true]")));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter(JsonValue.Create("Fabrikam")));
Assert.Equal<IEnumerable<string?>?>(["false"], (ImmutableArray<string?>?) new OpenIddictParameter(JsonValue.Create(false)));
Assert.Equal<IEnumerable<string?>?>(["42"], (ImmutableArray<string?>?) new OpenIddictParameter(JsonValue.Create(42)));
Assert.Equal<IEnumerable<string?>?>(["42"], (ImmutableArray<string?>?) new OpenIddictParameter(JsonValue.Create(42L)));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter(new JsonArray("Fabrikam")));
Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam")));
Assert.Equal<IEnumerable<string?>?>(["value", "42", "true"], (ImmutableArray<string?>?) 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<IEnumerable<string?>?>(array, (StringValues?) parameter);
}
[Fact]
public void StringValuesConverter_CanCreateParameterFromPrimitiveValues()
{
// Arrange, act and assert
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter("Fabrikam"));
Assert.Equal<IEnumerable<string?>?>(["false"], (StringValues?) new OpenIddictParameter(false));
Assert.Equal<IEnumerable<string?>?>(["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<IEnumerable<string?>?>(["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<JsonElement>(@"[""value"",[]]")));
Assert.Null((StringValues?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""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<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"{""field"":""Fabrikam""}").GetProperty("field"))); JsonSerializer.Deserialize<JsonElement>(@"{""field"":""Fabrikam""}").GetProperty("field")));
Assert.Equal<string?[]?>(["false"], (string?[]?) new OpenIddictParameter( Assert.Equal<IEnumerable<string?>?>(["false"], (StringValues?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"{""field"":false}").GetProperty("field"))); JsonSerializer.Deserialize<JsonElement>(@"{""field"":false}").GetProperty("field")));
Assert.Equal<string?[]?>(["42"], (string?[]?) new OpenIddictParameter( Assert.Equal<IEnumerable<string?>?>(["42"], (StringValues?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"{""field"":42}").GetProperty("field"))); JsonSerializer.Deserialize<JsonElement>(@"{""field"":42}").GetProperty("field")));
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter( Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""Fabrikam""]"))); JsonSerializer.Deserialize<JsonElement>(@"[""Fabrikam""]")));
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) new OpenIddictParameter( Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (StringValues?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""Contoso"",""Fabrikam""]"))); JsonSerializer.Deserialize<JsonElement>(@"[""Contoso"",""Fabrikam""]")));
Assert.Equal<string?[]?>(["value", "42", "true"], (string?[]?) new OpenIddictParameter( Assert.Equal<IEnumerable<string?>?>(["value", "42", "true"], (StringValues?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""value"",42,true]"))); JsonSerializer.Deserialize<JsonElement>(@"[""value"",42,true]")));
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter(JsonValue.Create("Fabrikam"))); Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(JsonValue.Create("Fabrikam")));
Assert.Equal<string?[]?>(["false"], (string?[]?) new OpenIddictParameter(JsonValue.Create(false))); Assert.Equal<IEnumerable<string?>?>(["false"], (StringValues?) new OpenIddictParameter(JsonValue.Create(false)));
Assert.Equal<string?[]?>(["42"], (string?[]?) new OpenIddictParameter(JsonValue.Create(42))); Assert.Equal<IEnumerable<string?>?>(["42"], (StringValues?) new OpenIddictParameter(JsonValue.Create(42)));
Assert.Equal<string?[]?>(["42"], (string?[]?) new OpenIddictParameter(JsonValue.Create(42L))); Assert.Equal<IEnumerable<string?>?>(["42"], (StringValues?) new OpenIddictParameter(JsonValue.Create(42L)));
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter(new JsonArray("Fabrikam"))); Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(new JsonArray("Fabrikam")));
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam"))); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (StringValues?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam")));
Assert.Equal<string?[]?>(["value", "42", "true"], (string?[]?) new OpenIddictParameter(new JsonArray("value", 42, true))); Assert.Equal<IEnumerable<string?>?>(["value", "42", "true"], (StringValues?) new OpenIddictParameter(new JsonArray("value", 42, true)));
} }
} }

30
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictRequestTests.cs

@ -5,7 +5,9 @@
* the license and the contributors participating to this project. * 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; using Xunit;
namespace OpenIddict.Abstractions.Tests.Primitives; namespace OpenIddict.Abstractions.Tests.Primitives;
@ -48,7 +50,7 @@ public class OpenIddictRequestTests
{ {
/* property: */ nameof(OpenIddictRequest.Claims), /* property: */ nameof(OpenIddictRequest.Claims),
/* name: */ Parameters.Claims, /* name: */ Parameters.Claims,
/* value: */ new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""userinfo"": {}}")) /* value: */ new OpenIddictParameter(JsonObject.Parse(@"{""userinfo"": {}}"))
}; };
yield return new object[] yield return new object[]
@ -216,7 +218,7 @@ public class OpenIddictRequestTests
{ {
/* property: */ nameof(OpenIddictRequest.Registration), /* property: */ nameof(OpenIddictRequest.Registration),
/* name: */ Parameters.Registration, /* name: */ Parameters.Registration,
/* value: */ new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""policy_uri"": ""http://www.fabrikam.com/policy""}")) /* value: */ new OpenIddictParameter(JsonObject.Parse(@"{""policy_uri"": ""http://www.fabrikam.com/policy""}"))
}; };
yield return new object[] yield return new object[]
@ -314,7 +316,16 @@ public class OpenIddictRequestTests
request.SetParameter(name, value); request.SetParameter(name, value);
// Act and assert // 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] [Theory]
@ -325,7 +336,16 @@ public class OpenIddictRequestTests
var request = new OpenIddictRequest(); var request = new OpenIddictRequest();
// Act // Act
typeof(OpenIddictRequest).GetProperty(property)!.SetValue(request, value.Value); var info = typeof(OpenIddictRequest).GetProperty(property)!;
if (typeof(ImmutableArray<string?>).IsAssignableFrom(info.PropertyType))
{
info.SetValue(request, ImmutableCollectionsMarshal.AsImmutableArray((string?[]?) value.GetRawValue()));
}
else
{
info.SetValue(request, value.GetRawValue());
}
// Assert // Assert
Assert.Equal(value, request.GetParameter(name)); Assert.Equal(value, request.GetParameter(name));

25
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictResponseTests.cs

@ -5,6 +5,9 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
using Xunit; using Xunit;
namespace OpenIddict.Abstractions.Tests.Primitives; namespace OpenIddict.Abstractions.Tests.Primitives;
@ -145,7 +148,16 @@ public class OpenIddictResponseTests
response.SetParameter(name, value); response.SetParameter(name, value);
// Act and assert // 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] [Theory]
@ -156,7 +168,16 @@ public class OpenIddictResponseTests
var response = new OpenIddictResponse(); var response = new OpenIddictResponse();
// Act // Act
typeof(OpenIddictResponse).GetProperty(property)!.SetValue(response, value.Value); var info = typeof(OpenIddictResponse).GetProperty(property)!;
if (typeof(ImmutableArray<string?>).IsAssignableFrom(info.PropertyType))
{
info.SetValue(response, ImmutableCollectionsMarshal.AsImmutableArray((string?[]?) value.GetRawValue()));
}
else
{
info.SetValue(response, value.GetRawValue());
}
// Assert // Assert
Assert.Equal(value, response.GetParameter(name)); Assert.Equal(value, response.GetParameter(name));

13
test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; 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(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["array_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind);
Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]);
Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["node_array_parameter"]);
Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]); Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]);
Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]);
Assert.IsType<JsonObject>((JsonNode?) response["node_object_parameter"]); Assert.IsType<JsonObject>((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(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["array_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind);
Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]);
Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["node_array_parameter"]);
Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]); Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]);
Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]);
Assert.IsType<JsonObject>((JsonNode?) response["node_object_parameter"]); Assert.IsType<JsonObject>((JsonNode?) response["node_object_parameter"]);
@ -741,8 +742,8 @@ public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServ
} }
var claims = result.Principal.Claims.GroupBy(claim => claim.Type) var claims = result.Principal.Claims.GroupBy(claim => claim.Type)
.Select(group => new KeyValuePair<string, string?[]?>( .Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToArray())); group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims))); await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims)));

9
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using AngleSharp.Html.Parser; using AngleSharp.Html.Parser;
@ -245,17 +246,17 @@ public class OpenIddictServerIntegrationTestClient : IAsyncDisposable
{ {
message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!, message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!,
request.GetParameters().ToDictionary( request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))); static parameter => (StringValues) parameter.Value));
} }
if (message.Method != HttpMethod.Get) if (message.Method != HttpMethod.Get)
{ {
message.Content = new FormUrlEncodedContent( message.Content = new FormUrlEncodedContent(
from parameter in request.GetParameters() from parameter in request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
select new KeyValuePair<string?, string?>(parameter.Key, value)); select new KeyValuePair<string?, string?>(parameter.Key, value));
} }

32
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs

@ -2514,11 +2514,7 @@ public abstract partial class OpenIddictServerIntegrationTests
.SetClaim(Claims.Subject, "Bob le Magnifique"); .SetClaim(Claims.Subject, "Bob le Magnifique");
context.Parameters["custom_parameter"] = "custom_value"; context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[] context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -2541,7 +2537,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(response.ErrorUri); Assert.Null(response.ErrorUri);
Assert.NotNull(response.AccessToken); Assert.NotNull(response.AccessToken);
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Theory] [Theory]
@ -2660,11 +2656,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -2683,7 +2675,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Fact] [Fact]
@ -5204,11 +5196,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Parameters["custom_parameter"] = "custom_value"; context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[] context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -5231,7 +5219,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(response.ErrorUri); Assert.Null(response.ErrorUri);
Assert.NotNull(response.RequestUri); Assert.NotNull(response.RequestUri);
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Fact] [Fact]
@ -5283,11 +5271,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -5306,6 +5290,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
} }

24
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs

@ -1027,11 +1027,7 @@ public abstract partial class OpenIddictServerIntegrationTests
context.Principal = new ClaimsPrincipal(new ClaimsIdentity()); context.Principal = new ClaimsPrincipal(new ClaimsIdentity());
context.Parameters["custom_parameter"] = "custom_value"; context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[] context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1051,7 +1047,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(response.ErrorUri); Assert.Null(response.ErrorUri);
Assert.NotNull(response.DeviceCode); Assert.NotNull(response.DeviceCode);
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Fact] [Fact]
@ -1116,11 +1112,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1136,7 +1128,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Theory] [Theory]
@ -1467,11 +1459,7 @@ public abstract partial class OpenIddictServerIntegrationTests
.SetClaim(Claims.Subject, "Bob le Magnifique"); .SetClaim(Claims.Subject, "Bob le Magnifique");
context.Parameters["custom_parameter"] = "custom_value"; context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[] context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1496,7 +1484,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Fact] [Fact]

69
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Net.Http; using System.Net.Http;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text.Json; using System.Text.Json;
@ -423,11 +424,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var methods = (string?[]?) response[Metadata.TokenEndpointAuthMethodsSupported]; var methods = (ImmutableArray<string?>?) response[Metadata.TokenEndpointAuthMethodsSupported];
// Assert // Assert
Assert.NotNull(methods); Assert.NotNull(methods);
Assert.Equal(3, methods.Length); Assert.Equal(3, methods.Value.Length);
Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods);
Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods);
Assert.Contains("custom", methods); Assert.Contains("custom", methods);
@ -465,11 +466,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var methods = (string?[]?) response[Metadata.IntrospectionEndpointAuthMethodsSupported]; var methods = (ImmutableArray<string?>?) response[Metadata.IntrospectionEndpointAuthMethodsSupported];
// Assert // Assert
Assert.NotNull(methods); Assert.NotNull(methods);
Assert.Equal(3, methods.Length); Assert.Equal(3, methods.Value.Length);
Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods);
Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods);
Assert.Contains("custom", methods); Assert.Contains("custom", methods);
@ -507,11 +508,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var methods = (string?[]?) response[Metadata.RevocationEndpointAuthMethodsSupported]; var methods = (ImmutableArray<string?>?) response[Metadata.RevocationEndpointAuthMethodsSupported];
// Assert // Assert
Assert.NotNull(methods); Assert.NotNull(methods);
Assert.Equal(3, methods.Length); Assert.Equal(3, methods.Value.Length);
Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods);
Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods);
Assert.Contains("custom", methods); Assert.Contains("custom", methods);
@ -550,11 +551,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var methods = (string?[]?) response[Metadata.DeviceAuthorizationEndpointAuthMethodsSupported]; var methods = (ImmutableArray<string?>?) response[Metadata.DeviceAuthorizationEndpointAuthMethodsSupported];
// Assert // Assert
Assert.NotNull(methods); Assert.NotNull(methods);
Assert.Equal(3, methods.Length); Assert.Equal(3, methods.Value.Length);
Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods);
Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods);
Assert.Contains("custom", methods); Assert.Contains("custom", methods);
@ -592,11 +593,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var methods = (string?[]?) response[Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported]; var methods = (ImmutableArray<string?>?) response[Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported];
// Assert // Assert
Assert.NotNull(methods); Assert.NotNull(methods);
Assert.Equal(3, methods.Length); Assert.Equal(3, methods.Value.Length);
Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods); Assert.Contains(ClientAuthenticationMethods.ClientSecretPost, methods);
Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods); Assert.Contains(ClientAuthenticationMethods.PrivateKeyJwt, methods);
Assert.Contains("custom", methods); Assert.Contains("custom", methods);
@ -617,11 +618,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var types = (string?[]?) response[Metadata.GrantTypesSupported]; var types = (ImmutableArray<string?>?) response[Metadata.GrantTypesSupported];
// Assert // Assert
Assert.NotNull(types); Assert.NotNull(types);
Assert.Equal(2, types.Length); Assert.Equal(2, types.Value.Length);
Assert.Contains(GrantTypes.AuthorizationCode, types); Assert.Contains(GrantTypes.AuthorizationCode, types);
Assert.Contains(GrantTypes.Password, types); Assert.Contains(GrantTypes.Password, types);
} }
@ -659,11 +660,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var methods = (string?[]?) response[Metadata.CodeChallengeMethodsSupported]; var methods = (ImmutableArray<string?>?) response[Metadata.CodeChallengeMethodsSupported];
// Assert // Assert
Assert.NotNull(methods); Assert.NotNull(methods);
Assert.Equal(2, methods.Length); Assert.Equal(2, methods.Value.Length);
Assert.Contains(CodeChallengeMethods.Sha256, methods); Assert.Contains(CodeChallengeMethods.Sha256, methods);
Assert.Contains(CodeChallengeMethods.Plain, methods); Assert.Contains(CodeChallengeMethods.Plain, methods);
} }
@ -701,11 +702,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var modes = (string?[]?) response[Metadata.ResponseModesSupported]; var modes = (ImmutableArray<string?>?) response[Metadata.ResponseModesSupported];
// Assert // Assert
Assert.NotNull(modes); Assert.NotNull(modes);
Assert.Equal(2, modes.Length); Assert.Equal(2, modes.Value.Length);
Assert.Contains(ResponseModes.FormPost, modes); Assert.Contains(ResponseModes.FormPost, modes);
Assert.Contains(ResponseModes.Fragment, modes); Assert.Contains(ResponseModes.Fragment, modes);
} }
@ -743,11 +744,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var types = (string?[]?) response[Metadata.ResponseTypesSupported]; var types = (ImmutableArray<string?>?) response[Metadata.ResponseTypesSupported];
// Assert // Assert
Assert.NotNull(types); Assert.NotNull(types);
Assert.Equal(2, types.Length); Assert.Equal(2, types.Value.Length);
Assert.Contains(ResponseTypes.Code, types); Assert.Contains(ResponseTypes.Code, types);
Assert.Contains(ResponseTypes.Code + ' ' + ResponseTypes.IdToken, types); Assert.Contains(ResponseTypes.Code + ' ' + ResponseTypes.IdToken, types);
} }
@ -785,11 +786,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var scopes = (string?[]?) response[Metadata.ScopesSupported]; var scopes = (ImmutableArray<string?>?) response[Metadata.ScopesSupported];
// Assert // Assert
Assert.NotNull(scopes); Assert.NotNull(scopes);
Assert.Equal(2, scopes.Length); Assert.Equal(2, scopes.Value.Length);
Assert.Contains(Scopes.OpenId, scopes); Assert.Contains(Scopes.OpenId, scopes);
Assert.Contains("custom_scope", scopes); Assert.Contains("custom_scope", scopes);
} }
@ -827,11 +828,11 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var claims = (string?[]?) response[Metadata.ClaimsSupported]; var claims = (ImmutableArray<string?>?) response[Metadata.ClaimsSupported];
// Assert // Assert
Assert.NotNull(claims); Assert.NotNull(claims);
Assert.Equal(2, claims.Length); Assert.Equal(2, claims.Value.Length);
Assert.Contains(Claims.Profile, claims); Assert.Contains(Claims.Profile, claims);
Assert.Contains("custom_claim", claims); Assert.Contains("custom_claim", claims);
} }
@ -850,7 +851,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var types = (string?[]?) response[Metadata.SubjectTypesSupported]; var types = (ImmutableArray<string?>?) response[Metadata.SubjectTypesSupported];
// Assert // Assert
Assert.NotNull(types); Assert.NotNull(types);
@ -874,7 +875,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var types = (string?[]?) response[Metadata.PromptValuesSupported]; var types = (ImmutableArray<string?>?) response[Metadata.PromptValuesSupported];
// Assert // Assert
Assert.NotNull(types); Assert.NotNull(types);
@ -905,7 +906,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var algorithms = (string?[]?) response[Metadata.IdTokenSigningAlgValuesSupported]; var algorithms = (ImmutableArray<string?>?) response[Metadata.IdTokenSigningAlgValuesSupported];
// Assert // Assert
Assert.NotNull(algorithms); Assert.NotNull(algorithms);
@ -927,7 +928,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var algorithms = (string?[]?) response[Metadata.IdTokenSigningAlgValuesSupported]; var algorithms = (ImmutableArray<string?>?) response[Metadata.IdTokenSigningAlgValuesSupported];
// Assert // Assert
Assert.NotNull(algorithms); Assert.NotNull(algorithms);
@ -953,7 +954,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act // Act
var response = await client.GetAsync("/.well-known/openid-configuration"); var response = await client.GetAsync("/.well-known/openid-configuration");
var algorithms = (string?[]?) response[Metadata.IdTokenSigningAlgValuesSupported]; var algorithms = (ImmutableArray<string?>?) response[Metadata.IdTokenSigningAlgValuesSupported];
// Assert // Assert
Assert.NotNull(algorithms); Assert.NotNull(algorithms);
@ -1131,11 +1132,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1629,11 +1626,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1646,6 +1639,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
} }

16
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs

@ -4258,11 +4258,7 @@ public abstract partial class OpenIddictServerIntegrationTests
.SetClaim(Claims.Subject, "Bob le Magnifique"); .SetClaim(Claims.Subject, "Bob le Magnifique");
context.Parameters["custom_parameter"] = "custom_value"; context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[] context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -4284,7 +4280,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(response.ErrorUri); Assert.Null(response.ErrorUri);
Assert.NotNull(response.AccessToken); Assert.NotNull(response.AccessToken);
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Fact] [Fact]
@ -4353,11 +4349,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -4375,6 +4367,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
} }

10
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(JsonValueKind.True, ((JsonElement) response["boolean_claim"]).ValueKind);
Assert.Equal(42, (long) response["integer_claim"]); Assert.Equal(42, (long) response["integer_claim"]);
Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_claim"]).ValueKind); Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_claim"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["array_claim"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["array_claim"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_claim"]).ValueKind); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_claim"]).ValueKind);
Assert.Equal("value", (string?) response["object_claim"]?["parameter"]); Assert.Equal("value", (string?) response["object_claim"]?["parameter"]);
Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_claim"]).ValueKind); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_claim"]).ValueKind);
@ -1815,11 +1815,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1835,6 +1831,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
} }

8
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs

@ -316,8 +316,8 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal<string?[]?>(["Fabrikam", "Contoso"], (string?[]?) response[Claims.Audience]); Assert.Equal<IEnumerable<string?>?>(["Fabrikam", "Contoso"], (ImmutableArray<string?>?) response[Claims.Audience]);
Assert.Equal<string?[]?>(["Fabrikam", "Contoso"], (string?[]?) response[Claims.Private.Audience]); Assert.Equal<IEnumerable<string?>?>(["Fabrikam", "Contoso"], (ImmutableArray<string?>?) response[Claims.Private.Audience]);
} }
[Fact] [Fact]
@ -415,7 +415,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal<string?[]?>([Scopes.OpenId, Scopes.Profile], (string?[]?) response[Claims.Private.Scope]); Assert.Equal<IEnumerable<string?>?>([Scopes.OpenId, Scopes.Profile], (ImmutableArray<string?>?) response[Claims.Private.Scope]);
} }
[Fact] [Fact]
@ -464,7 +464,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal<string?[]?>([Scopes.OpenId, Scopes.Profile], (string?[]?) response[Claims.Private.Scope]); Assert.Equal<IEnumerable<string?>?>([Scopes.OpenId, Scopes.Profile], (ImmutableArray<string?>?) response[Claims.Private.Scope]);
} }
[Fact] [Fact]

9
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Net.Http; using System.Net.Http;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -1146,11 +1147,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1166,6 +1163,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
} }

16
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs

@ -1044,11 +1044,7 @@ public abstract partial class OpenIddictServerIntegrationTests
context.SignOut(); context.SignOut();
context.Parameters["custom_parameter"] = "custom_value"; context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[] context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1064,7 +1060,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Fact] [Fact]
@ -1126,11 +1122,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -1146,7 +1138,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
[Fact] [Fact]

10
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs

@ -387,7 +387,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Equal(3, response.Count); Assert.Equal(3, response.Count);
Assert.Equal("http://localhost/", (string?) response[Claims.Issuer]); Assert.Equal("http://localhost/", (string?) response[Claims.Issuer]);
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]); Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal<string?[]?>(["Fabrikam", "Contoso"], (string?[]?) response[Claims.Audience]); Assert.Equal<IEnumerable<string?>?>(["Fabrikam", "Contoso"], (ImmutableArray<string?>?) response[Claims.Audience]);
} }
[Fact] [Fact]
@ -788,11 +788,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
{ {
context.Response["custom_parameter"] = "custom_value"; context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[] context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
{
"custom_value_1",
"custom_value_2"
};
return default; return default;
})); }));
@ -808,6 +804,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert // Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]); Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<string?[]?>(["custom_value_1", "custom_value_2"], (string?[]?) response["parameter_with_multiple_values"]); Assert.Equal<IEnumerable<string?>?>(["custom_value_1", "custom_value_2"], (ImmutableArray<string?>?) response["parameter_with_multiple_values"]);
} }
} }

8
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(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["array_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind);
Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]);
Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["node_array_parameter"]);
Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]); Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]);
Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]);
Assert.IsType<JsonObject>((JsonNode?) response["node_object_parameter"]); Assert.IsType<JsonObject>((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(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["array_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Array, ((JsonElement) response["array_parameter"]).ValueKind);
Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["object_parameter"]?["parameter"]);
Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Object, ((JsonElement) response["object_parameter"]).ValueKind);
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) response["node_array_parameter"]); Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["node_array_parameter"]);
Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]); Assert.IsType<JsonArray>((JsonNode?) response["node_array_parameter"]);
Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]); Assert.Equal("value", (string?) response["node_object_parameter"]?["parameter"]);
Assert.IsType<JsonObject>((JsonNode?) response["node_object_parameter"]); Assert.IsType<JsonObject>((JsonNode?) response["node_object_parameter"]);

9
test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -254,7 +255,7 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind); Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["json_parameter"]); Assert.Equal(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["json_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["json_parameter"]).ValueKind); 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(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]); Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind); Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["json_parameter"]); Assert.Equal(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["json_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["json_parameter"]).ValueKind); 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) var claims = result.Identity.Claims.GroupBy(claim => claim.Type)
.Select(group => new KeyValuePair<string, string?[]?>( .Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToArray())); group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims))); await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims)));

5
test/OpenIddict.Validation.AspNetCore.IntegrationTests/OpenIddictValidationAspNetCoreIntegrationTests.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
@ -184,8 +185,8 @@ public partial class OpenIddictValidationAspNetCoreIntegrationTests : OpenIddict
} }
var claims = result.Principal.Claims.GroupBy(claim => claim.Type) var claims = result.Principal.Claims.GroupBy(claim => claim.Type)
.Select(group => new KeyValuePair<string, string?[]?>( .Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToArray())); group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims))); await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims)));

10
test/OpenIddict.Validation.IntegrationTests/OpenIddictValidationIntegrationTestClient.cs

@ -4,8 +4,10 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Runtime.InteropServices;
using AngleSharp.Html.Parser; using AngleSharp.Html.Parser;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using OpenIddict.Extensions; using OpenIddict.Extensions;
@ -245,17 +247,17 @@ public class OpenIddictValidationIntegrationTestClient : IAsyncDisposable
{ {
message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!, message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!,
request.GetParameters().ToDictionary( request.GetParameters().ToDictionary(
parameter => parameter.Key, static parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value))); static parameter => (StringValues) parameter.Value));
} }
if (message.Method != HttpMethod.Get) if (message.Method != HttpMethod.Get)
{ {
message.Content = new FormUrlEncodedContent( message.Content = new FormUrlEncodedContent(
from parameter in request.GetParameters() from parameter in request.GetParameters()
let values = (string?[]?) parameter.Value let values = (ImmutableArray<string?>?) parameter.Value
where values is not null where values is not null
from value in values from value in values.GetValueOrDefault()
select new KeyValuePair<string?, string?>(parameter.Key, value)); select new KeyValuePair<string?, string?>(parameter.Key, value));
} }

5
test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -169,8 +170,8 @@ public partial class OpenIddictValidationOwinIntegrationTests : OpenIddictValida
} }
var claims = result.Identity.Claims.GroupBy(claim => claim.Type) var claims = result.Identity.Claims.GroupBy(claim => claim.Type)
.Select(group => new KeyValuePair<string, string?[]?>( .Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToArray())); group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims))); await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims)));

Loading…
Cancel
Save