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 11 months 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
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '9.0')) ">
<DefineConstants>$(DefineConstants);SUPPORTS_CERTIFICATE_LOADER</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_JSON_ELEMENT_DEEP_EQUALS</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_JSON_ELEMENT_PROPERTY_COUNT</DefineConstants>
</PropertyGroup>
<PropertyGroup

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

@ -639,14 +639,14 @@ public static class OpenIddictExtensions
/// </summary>
/// <param name="identity">The identity.</param>
/// <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)
{
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))
{
@ -676,14 +676,14 @@ public static class OpenIddictExtensions
/// </summary>
/// <param name="principal">The principal.</param>
/// <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)
{
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))
{
@ -714,7 +714,8 @@ public static class OpenIddictExtensions
/// <param name="identity">The identity.</param>
/// <param name="destinations">The destinations, as a flattened dictionary.</param>
/// <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)
{
@ -743,7 +744,8 @@ public static class OpenIddictExtensions
/// <param name="principal">The principal.</param>
/// <param name="destinations">The destinations, as a flattened dictionary.</param>
/// <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)
{

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

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

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

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

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

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@ -79,7 +80,7 @@ public class OpenIddictRequest : OpenIddictMessage
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <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)
{
}
@ -134,18 +135,18 @@ public class OpenIddictRequest : OpenIddictMessage
/// <summary>
/// Gets or sets the "audience" parameters.
/// </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);
}
/// <summary>
/// Gets or sets the "claims" parameter.
/// </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);
}
@ -379,9 +380,9 @@ public class OpenIddictRequest : OpenIddictMessage
/// <summary>
/// Gets or sets the "resource" parameters.
/// </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);
}
@ -442,9 +443,9 @@ public class OpenIddictRequest : OpenIddictMessage
/// <summary>
/// Gets or sets the "registration" parameter.
/// </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);
}

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

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Text.Json;
@ -78,7 +79,7 @@ public class OpenIddictResponse : OpenIddictMessage
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <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)
{
}

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

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

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

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

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

@ -8,6 +8,7 @@ using System.Buffers.Binary;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
@ -622,13 +623,17 @@ public static partial class OpenIddictClientAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch
{
OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value),
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
};
@ -943,13 +948,17 @@ public static partial class OpenIddictClientAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch
{
OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value),
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => 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.
var version = reader.ReadInt32();
if (version != 5)
if (version is not 5)
{
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.
foreach (var (key, value) in
from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value))
{

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.
foreach (var (key, value) in
from parameter in context.Request.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value))
{

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

@ -14,6 +14,7 @@ using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Extensions;
using Owin;
@ -265,7 +266,10 @@ public static partial class OpenIddictClientOwinHandlers
if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase))
{
context.Transaction.Request = new OpenIddictRequest(request.Query);
context.Transaction.Request = new OpenIddictRequest(
from parameter in request.Query
let values = new StringValues(parameter.Value)
select new KeyValuePair<string, StringValues>(parameter.Key, values));
}
else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase))
@ -296,7 +300,10 @@ public static partial class OpenIddictClientOwinHandlers
return;
}
context.Transaction.Request = new OpenIddictRequest(await request.ReadFormAsync());
context.Transaction.Request = new OpenIddictRequest(
from parameter in await request.ReadFormAsync()
let values = new StringValues(parameter.Value)
select new KeyValuePair<string, StringValues>(parameter.Key, values));
}
else

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

@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
@ -78,7 +79,9 @@ public sealed class OpenIddictClientSystemIntegrationActivationHandler : IHosted
}
#endif
// Otherwise, try to extract the protocol activation from the command line arguments.
if (GetProtocolActivationUriFromCommandLineArguments(Environment.GetCommandLineArgs()) is Uri value)
var arguments = ImmutableCollectionsMarshal.AsImmutableArray(Environment.GetCommandLineArgs());
if (GetProtocolActivationUriFromCommandLineArguments(arguments) is Uri value)
{
return new OpenIddictClientSystemIntegrationActivation(value);
}

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

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

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

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

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

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
@ -378,7 +379,7 @@ public static class OpenIddictClientSystemIntegrationHelpers
/// The <see cref="Uri"/> if the application instance was activated
/// via a protocol activation, <see langword="null"/> otherwise.
/// </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
// 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))
{
parameters[parameter.Key] = parameter.Value.Count switch
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
parameters[parameter.Key] = parameter.Value;
}
}
@ -86,12 +81,7 @@ public sealed class OpenIddictClientSystemIntegrationService
{
foreach (var parameter in OpenIddictHelpers.ParseFragment(intent.Data.Fragment))
{
parameters[parameter.Key] = parameter.Value.Count switch
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
};
parameters[parameter.Key] = parameter.Value;
}
}

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

@ -1044,8 +1044,8 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
{
request.RequestUri = OpenIddictHelpers.AddQueryStringParameters(request.RequestUri,
context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value)));
static parameter => parameter.Key,
static parameter => (StringValues) parameter.Value));
}
// For POST requests, attach the request parameters to the request form by default.
@ -1053,9 +1053,9 @@ public static partial class OpenIddictClientSystemNetHttpHandlers
{
request.Content = new FormUrlEncodedContent(
from parameter in context.Transaction.Request.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
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:
JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element &&
element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element),
element.ValueKind is JsonValueKind.Array &&
OpenIddictHelpers.ValidateArrayElements(element, JsonValueKind.Object),
// Parameters that are not in the well-known list can be of any type.
_ => true
};
static bool ValidateObjectArray(JsonElement element)
{
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind is not JsonValueKind.Object)
{
return false;
}
}
return true;
}
}
}

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

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

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

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

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

@ -7,6 +7,7 @@
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
@ -257,13 +258,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch
{
OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value),
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
};
@ -354,13 +359,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch
{
OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value),
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115))
};
@ -413,13 +422,17 @@ public static partial class OpenIddictServerAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch
{
OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value),
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => 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.
var version = reader.ReadInt32();
if (version != 5)
if (version is not 5)
{
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);
foreach (var (key, value) in
from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value))
{
@ -180,9 +180,9 @@ public static partial class OpenIddictServerOwinHandlers
// For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in
from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value))
{
@ -256,9 +256,9 @@ public static partial class OpenIddictServerOwinHandlers
// For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in
from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value))
{
@ -317,9 +317,9 @@ public static partial class OpenIddictServerOwinHandlers
// For consistency, multiple parameters with the same name are also supported by this endpoint.
foreach (var (key, value) in
from parameter in context.Response.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
where !string.IsNullOrEmpty(value)
select (parameter.Key, Value: value))
{

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

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

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

@ -13,6 +13,7 @@ using System.Text.Encodings.Web;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using OpenIddict.Extensions;
using Owin;
using static OpenIddict.Server.Owin.OpenIddictServerOwinConstants;
@ -524,7 +525,10 @@ public static partial class OpenIddictServerOwinHandlers
if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase))
{
context.Transaction.Request = new OpenIddictRequest(request.Query);
context.Transaction.Request = new OpenIddictRequest(
from parameter in request.Query
let values = new StringValues(parameter.Value)
select new KeyValuePair<string, StringValues>(parameter.Key, values));
}
else
@ -575,7 +579,10 @@ public static partial class OpenIddictServerOwinHandlers
if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase))
{
context.Transaction.Request = new OpenIddictRequest(request.Query);
context.Transaction.Request = new OpenIddictRequest(
from parameter in request.Query
let values = new StringValues(parameter.Value)
select new KeyValuePair<string, StringValues>(parameter.Key, values));
}
else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase))
@ -606,7 +613,10 @@ public static partial class OpenIddictServerOwinHandlers
return;
}
context.Transaction.Request = new OpenIddictRequest(await request.ReadFormAsync());
context.Transaction.Request = new OpenIddictRequest(
from parameter in await request.ReadFormAsync()
let values = new StringValues(parameter.Value)
select new KeyValuePair<string, StringValues>(parameter.Key, values));
}
else
@ -681,7 +691,10 @@ public static partial class OpenIddictServerOwinHandlers
return;
}
context.Transaction.Request = new OpenIddictRequest(await request.ReadFormAsync());
context.Transaction.Request = new OpenIddictRequest(
from parameter in await request.ReadFormAsync()
let values = new StringValues(parameter.Value)
select new KeyValuePair<string, StringValues>(parameter.Key, values));
}
else

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

@ -6,6 +6,7 @@
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace OpenIddict.Server;
@ -102,7 +103,7 @@ public static partial class OpenIddictServerEvents
/// Note: this value should only be populated if the "address"
/// scope was requested and accepted by the resource owner.
/// </summary>
public JsonElement Address { get; set; }
public JsonObject? Address { get; set; }
/// <summary>
/// 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.PushedAuthorizationRequestEndpoint] = notification.PushedAuthorizationEndpoint?.AbsoluteUri,
[Metadata.JwksUri] = notification.JsonWebKeySetEndpoint?.AbsoluteUri,
[Metadata.GrantTypesSupported] = notification.GrantTypes.ToArray(),
[Metadata.ResponseTypesSupported] = notification.ResponseTypes.ToArray(),
[Metadata.ResponseModesSupported] = notification.ResponseModes.ToArray(),
[Metadata.ScopesSupported] = notification.Scopes.ToArray(),
[Metadata.ClaimsSupported] = notification.Claims.ToArray(),
[Metadata.IdTokenSigningAlgValuesSupported] = notification.IdTokenSigningAlgorithms.ToArray(),
[Metadata.CodeChallengeMethodsSupported] = notification.CodeChallengeMethods.ToArray(),
[Metadata.SubjectTypesSupported] = notification.SubjectTypes.ToArray(),
[Metadata.PromptValuesSupported] = notification.PromptValues.ToArray(),
[Metadata.TokenEndpointAuthMethodsSupported] = notification.TokenEndpointAuthenticationMethods.ToArray(),
[Metadata.IntrospectionEndpointAuthMethodsSupported] = notification.IntrospectionEndpointAuthenticationMethods.ToArray(),
[Metadata.RevocationEndpointAuthMethodsSupported] = notification.RevocationEndpointAuthenticationMethods.ToArray(),
[Metadata.DeviceAuthorizationEndpointAuthMethodsSupported] = notification.DeviceAuthorizationEndpointAuthenticationMethods.ToArray(),
[Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported] = notification.PushedAuthorizationEndpointAuthenticationMethods.ToArray(),
[Metadata.GrantTypesSupported] = notification.GrantTypes.ToImmutableArray<string?>(),
[Metadata.ResponseTypesSupported] = notification.ResponseTypes.ToImmutableArray<string?>(),
[Metadata.ResponseModesSupported] = notification.ResponseModes.ToImmutableArray<string?>(),
[Metadata.ScopesSupported] = notification.Scopes.ToImmutableArray<string?>(),
[Metadata.ClaimsSupported] = notification.Claims.ToImmutableArray<string?>(),
[Metadata.IdTokenSigningAlgValuesSupported] = notification.IdTokenSigningAlgorithms.ToImmutableArray<string?>(),
[Metadata.CodeChallengeMethodsSupported] = notification.CodeChallengeMethods.ToImmutableArray<string?>(),
[Metadata.SubjectTypesSupported] = notification.SubjectTypes.ToImmutableArray<string?>(),
[Metadata.PromptValuesSupported] = notification.PromptValues.ToImmutableArray<string?>(),
[Metadata.TokenEndpointAuthMethodsSupported] = notification.TokenEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.IntrospectionEndpointAuthMethodsSupported] = notification.IntrospectionEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.RevocationEndpointAuthMethodsSupported] = notification.RevocationEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.DeviceAuthorizationEndpointAuthMethodsSupported] = notification.DeviceAuthorizationEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.PushedAuthorizationRequestEndpointAuthMethodsSupported] = notification.PushedAuthorizationEndpointAuthenticationMethods.ToImmutableArray<string?>(),
[Metadata.RequirePushedAuthorizationRequests] = notification.RequirePushedAuthorizationRequests
};

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

@ -268,7 +268,7 @@ public static partial class OpenIddictServerHandlers
break;
default:
response[Claims.Audience] = notification.Audiences.ToArray();
response[Claims.Audience] = notification.Audiences.ToImmutableArray<string?>();
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).
//
// 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
// is retrieved as a Dictionary<string, string[]>, which is supported by both Wilson 6.x and 7.x.
// TryGetPayloadValue() API. Since ImmutableDictionary<string, string[]> is not supported, the value is
// retrieved as a Dictionary<string, string[]> and converted to ImmutableDictionary<string, ImmutableArray<string>.
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);

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

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

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

@ -7,6 +7,7 @@
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
@ -368,13 +369,17 @@ public static partial class OpenIddictValidationAspNetCoreHandlers
context.Parameters[parameter.Key] = parameter.Value switch
{
OpenIddictParameter value => value,
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
string[] value => new OpenIddictParameter(value),
JsonElement value => new OpenIddictParameter(value),
JsonNode value => new OpenIddictParameter(value),
bool value => new OpenIddictParameter(value),
int value => new OpenIddictParameter(value),
long value => new OpenIddictParameter(value),
string value => new OpenIddictParameter(value),
ImmutableArray<string?> value => new OpenIddictParameter(value),
string?[] value => new(ImmutableCollectionsMarshal.AsImmutableArray(value)),
IEnumerable<string?> value => new OpenIddictParameter([.. value]),
_ => 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.
var version = reader.ReadInt32();
if (version != 5)
if (version is not 5)
{
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,
context.Transaction.Request.GetParameters().ToDictionary(
parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value)));
static parameter => parameter.Key,
static parameter => (StringValues) parameter.Value));
}
// For POST requests, attach the request parameters to the request form by default.
@ -577,9 +577,9 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers
{
request.Content = new FormUrlEncodedContent(
from parameter in context.Transaction.Request.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
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:
JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element &&
element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element),
element.ValueKind is JsonValueKind.Array &&
OpenIddictHelpers.ValidateArrayElements(element, JsonValueKind.Object),
// Parameters that are not in the well-known list can be of any type.
_ => true
};
static bool ValidateObjectArray(JsonElement element)
{
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind is not JsonValueKind.Object)
{
return false;
}
}
return true;
}
}
}

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

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

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

@ -1125,8 +1125,8 @@ public class OpenIddictExtensionsTests
// Assert
Assert.Equal(2, destinations.Count);
Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], destinations[Claims.Name]);
Assert.Equal<string[]>([Destinations.IdentityToken], destinations[Claims.Email]);
Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], [.. destinations[Claims.Name]]);
Assert.Equal<string[]>([Destinations.IdentityToken], [.. destinations[Claims.Email]]);
}
[Fact]
@ -1159,8 +1159,8 @@ public class OpenIddictExtensionsTests
// Assert
Assert.Equal(2, destinations.Count);
Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], destinations[Claims.Name]);
Assert.Equal<string[]>([Destinations.IdentityToken], destinations[Claims.Email]);
Assert.Equal<string[]>([Destinations.AccessToken, Destinations.IdentityToken], [.. destinations[Claims.Name]]);
Assert.Equal<string[]>([Destinations.IdentityToken], [.. destinations[Claims.Email]]);
}
[Fact]
@ -1192,7 +1192,7 @@ public class OpenIddictExtensionsTests
{
// Arrange
var identity = new ClaimsIdentity();
var destinations = (ImmutableDictionary<string, string[]>) null!;
var destinations = (ImmutableDictionary<string, ImmutableArray<string>>) null!;
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => identity.SetDestinations(destinations));
@ -1205,7 +1205,7 @@ public class OpenIddictExtensionsTests
{
// Arrange
var principal = new ClaimsPrincipal(new ClaimsIdentity());
var destinations = (ImmutableDictionary<string, string[]>) null!;
var destinations = (ImmutableDictionary<string, ImmutableArray<string>>) null!;
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => principal.SetDestinations(destinations));
@ -1226,7 +1226,7 @@ public class OpenIddictExtensionsTests
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.Email, [Destinations.IdentityToken]);
destinations.Add(Claims.Nonce, []);
@ -1253,7 +1253,7 @@ public class OpenIddictExtensionsTests
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));
var destinations = ImmutableDictionary.CreateBuilder<string, string[]>(StringComparer.Ordinal);
var destinations = ImmutableDictionary.CreateBuilder<string, ImmutableArray<string>>(StringComparer.Ordinal);
destinations.Add(Claims.Name, [Destinations.AccessToken, Destinations.IdentityToken]);
destinations.Add(Claims.Email, [Destinations.IdentityToken]);
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.
*/
using System.Collections.Immutable;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
@ -98,7 +99,8 @@ public class OpenIddictMessageTests
// Assert
Assert.Equal(1, message.Count);
Assert.Equal<string?[]?>(["Fabrikam", "Contoso"], (string?[]?) message.GetParameter("parameter"));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam", "Contoso"],
(ImmutableArray<string?>?) message.GetParameter("parameter"));
}
[Fact]
@ -107,12 +109,13 @@ public class OpenIddictMessageTests
// Arrange and act
var message = new OpenIddictMessage(
[
new KeyValuePair<string, string?[]?>("parameter", ["Fabrikam", "Contoso"])
new KeyValuePair<string, ImmutableArray<string?>?>("parameter", ["Fabrikam", "Contoso"])
]);
// Assert
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]
@ -121,12 +124,12 @@ public class OpenIddictMessageTests
// Arrange and act
var message = new OpenIddictMessage(
[
new KeyValuePair<string, string?[]?>("parameter", ["Fabrikam"])
new KeyValuePair<string, ImmutableArray<string?>?>("parameter", ["Fabrikam"])
]);
// Assert
Assert.Equal(1, message.Count);
Assert.Equal("Fabrikam", message.GetParameter("parameter")?.Value);
Assert.Equal("Fabrikam", (string?) message.GetParameter("parameter"));
}
[Theory]
@ -423,7 +426,7 @@ public class OpenIddictMessageTests
// Act and assert
Assert.True(message.TryGetParameter("parameter", out var parameter));
Assert.Equal(42, (long?) parameter.Value);
Assert.Equal(42, (long?) parameter);
}
[Fact]
@ -434,7 +437,7 @@ public class OpenIddictMessageTests
// Act and assert
Assert.False(message.TryGetParameter("parameter", out OpenIddictParameter parameter));
Assert.Null(parameter.Value);
Assert.Equal(default, parameter);
}
[Fact]
@ -507,7 +510,7 @@ public class OpenIddictMessageTests
// Arrange
var message = new OpenIddictMessage
{
["redirect_uris"] = new[] { "https://abc.org/callback" },
["redirect_uris"] = new OpenIddictParameter(["https://abc.org/callback"]),
["client_name"] = "My Example Client"
};

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

@ -4,9 +4,12 @@
* the license and the contributors participating to this project.
*/
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.Primitives;
using Xunit;
namespace OpenIddict.Abstractions.Tests.Primitives;
@ -191,9 +194,9 @@ public class OpenIddictParameterTests
var parameter = new OpenIddictParameter(["Fabrikam", "Contoso"]);
// Act and assert
Assert.True(parameter.Equals(new string[] { "Fabrikam", "Contoso" }));
Assert.False(parameter.Equals(new string[] { "Fabrikam" }));
Assert.False(parameter.Equals(new string[] { "Contoso", "Fabrikam" }));
Assert.True(parameter.Equals(new OpenIddictParameter(["Fabrikam", "Contoso"])));
Assert.False(parameter.Equals(new OpenIddictParameter(["Fabrikam"])));
Assert.False(parameter.Equals(new OpenIddictParameter(["Contoso", "Fabrikam"])));
}
[Fact]
@ -203,11 +206,11 @@ public class OpenIddictParameterTests
var parameter = new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"));
// Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("{}")));
Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("{}"))));
Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3))));
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonArray())));
@ -223,11 +226,11 @@ public class OpenIddictParameterTests
var parameter = new OpenIddictParameter(new JsonArray(0, 1, 2, 3));
// Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[0,1,2]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>("{}")));
Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[0,1,2]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("[3,2,1,0]"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>("{}"))));
Assert.True(parameter.Equals(new OpenIddictParameter(new JsonArray(0, 1, 2, 3))));
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonArray())));
@ -243,11 +246,11 @@ public class OpenIddictParameterTests
var parameter = new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"));
// Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"[]")));
Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{}"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"[]"))));
Assert.True(parameter.Equals(new OpenIddictParameter(new JsonObject
{
@ -285,23 +288,23 @@ public class OpenIddictParameterTests
});
// Act and assert
Assert.True(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}")));
Assert.False(parameter.Equals(JsonSerializer.Deserialize<JsonElement>(@"[]")));
Assert.True(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2,3]}"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{}"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":""value""}"))));
Assert.False(parameter.Equals(new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""field"":[0,1,2]}"))));
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)
}));
})));
Assert.False(parameter.Equals(new JsonObject()));
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject())));
Assert.False(parameter.Equals(new JsonObject
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject
{
["field"] = JsonValue.Create("value")
}));
})));
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject
{
@ -309,20 +312,20 @@ public class OpenIddictParameterTests
@"{""field"":""value""}").GetProperty("field"))
})));
Assert.False(parameter.Equals(new JsonObject
Assert.False(parameter.Equals(new OpenIddictParameter(new JsonObject
{
["field"] = new JsonArray(0, 1, 2)
}));
})));
Assert.True(parameter.Equals(JsonValue.Create(new Dictionary<string, object>
Assert.True(parameter.Equals(new OpenIddictParameter(JsonValue.Create(new Dictionary<string, object>
{
["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 }
})));
}))));
}
[Fact]
@ -797,11 +800,7 @@ public class OpenIddictParameterTests
public void GetNamedParameters_ReturnsEmptyDictionaryForArrays()
{
// Arrange
var parameters = new[]
{
"Fabrikam",
"Contoso"
};
ImmutableArray<string?> parameters = ["Fabrikam", "Contoso"];
var parameter = new OpenIddictParameter(parameters);
@ -924,11 +923,7 @@ public class OpenIddictParameterTests
public void GetUnnamedParameters_ReturnsExpectedParametersForArrays()
{
// Arrange
var parameters = new[]
{
"Fabrikam",
"Contoso"
};
ImmutableArray<string?> parameters = ["Fabrikam", "Contoso"];
var parameter = new OpenIddictParameter(parameters);
@ -1024,7 +1019,7 @@ public class OpenIddictParameterTests
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((bool?) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((long?) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((string?) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((string?[]?) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((ImmutableArray<string?>?) null)));
}
[Fact]
@ -1502,11 +1497,11 @@ public class OpenIddictParameterTests
public void BoolConverter_CanCreateParameterFromBooleanValue()
{
// Arrange, act and assert
Assert.True((bool?) new OpenIddictParameter(true).Value);
Assert.True((bool?) new OpenIddictParameter((bool?) true).Value);
Assert.True((bool?) new OpenIddictParameter(true));
Assert.True((bool?) new OpenIddictParameter((bool?) true));
Assert.False((bool?) new OpenIddictParameter(false).Value);
Assert.False((bool?) new OpenIddictParameter((bool?) false).Value);
Assert.False((bool?) new OpenIddictParameter(false));
Assert.False((bool?) new OpenIddictParameter((bool?) false));
}
[Fact]
@ -1898,8 +1893,8 @@ public class OpenIddictParameterTests
public void LongConverter_CanCreateParameterFromLongValue()
{
// Arrange, act and assert
Assert.Equal(42, (long?) new OpenIddictParameter(42).Value);
Assert.Equal(42, (long?) new OpenIddictParameter((long?) 42).Value);
Assert.Equal(42, (long?) new OpenIddictParameter(42));
Assert.Equal(42, (long?) new OpenIddictParameter((long?) 42));
}
[Fact]
@ -1970,7 +1965,7 @@ public class OpenIddictParameterTests
public void StringConverter_CanCreateParameterFromStringValue()
{
// Arrange, act and assert
Assert.Equal("Fabrikam", (string?) new OpenIddictParameter("Fabrikam").Value);
Assert.Equal("Fabrikam", (string?) new OpenIddictParameter("Fabrikam"));
}
[Fact]
@ -2041,77 +2036,155 @@ public class OpenIddictParameterTests
public void StringArrayConverter_CanCreateParameterFromArray()
{
// Arrange
var array = new[] { "Fabrikam", "Contoso" };
string?[] array = ["Fabrikam", "Contoso"];
// Act
var parameter = new OpenIddictParameter(array);
var parameter = new OpenIddictParameter([.. array]);
// Assert
Assert.Same(array, parameter.Value);
Assert.Equal<IEnumerable<string?>?>(array, (ImmutableArray<string?>?) parameter);
}
[Fact]
public void StringArrayConverter_CanCreateParameterFromPrimitiveValues()
{
// Arrange, act and assert
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter("Fabrikam"));
Assert.Equal<string?[]?>(["false"], (string?[]?) new OpenIddictParameter(false));
Assert.Equal<string?[]?>(["42"], (string?[]?) new OpenIddictParameter(42));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter("Fabrikam"));
Assert.Equal<IEnumerable<string?>?>(["false"], (ImmutableArray<string?>?) new OpenIddictParameter(false));
Assert.Equal<IEnumerable<string?>?>(["42"], (ImmutableArray<string?>?) new OpenIddictParameter(42));
}
[Fact]
public void StringArrayConverter_ReturnsDefaultValueForNullValues()
{
// Arrange, act and assert
Assert.Null((string?[]?) new OpenIddictParameter());
Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter());
}
[Fact]
public void StringArrayConverter_ReturnsSingleElementArrayForStringValue()
{
// Arrange, act and assert
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter("Fabrikam"));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (ImmutableArray<string?>?) new OpenIddictParameter("Fabrikam"));
}
[Fact]
public void StringArrayConverter_ReturnsDefaultValueForUnsupportedJsonValues()
{
// Arrange, act and assert
Assert.Null((string?[]?) new OpenIddictParameter(new JsonElement()));
Assert.Null((string?[]?) new OpenIddictParameter(
Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(new JsonElement()));
Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""value"",[]]")));
Assert.Null((string?[]?) new OpenIddictParameter(
Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(
JsonSerializer.Deserialize<JsonElement>(@"[""value"",{}]")));
Assert.Null((string?[]?) new OpenIddictParameter((JsonNode?) null));
Assert.Null((string?[]?) new OpenIddictParameter(new JsonArray("value", new JsonArray())));
Assert.Null((string?[]?) new OpenIddictParameter(new JsonArray("value", new JsonObject())));
Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter((JsonNode?) null));
Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(new JsonArray("value", new JsonArray())));
Assert.Null((ImmutableArray<string?>?) new OpenIddictParameter(new JsonArray("value", new JsonObject())));
}
[Fact]
public void StringArrayConverter_CanConvertFromJsonValues()
{
// 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")));
Assert.Equal<string?[]?>(["false"], (string?[]?) new OpenIddictParameter(
Assert.Equal<IEnumerable<string?>?>(["false"], (StringValues?) new OpenIddictParameter(
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")));
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter(
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(
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""]")));
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]")));
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter(JsonValue.Create("Fabrikam")));
Assert.Equal<string?[]?>(["false"], (string?[]?) new OpenIddictParameter(JsonValue.Create(false)));
Assert.Equal<string?[]?>(["42"], (string?[]?) new OpenIddictParameter(JsonValue.Create(42)));
Assert.Equal<string?[]?>(["42"], (string?[]?) new OpenIddictParameter(JsonValue.Create(42L)));
Assert.Equal<string?[]?>(["Fabrikam"], (string?[]?) new OpenIddictParameter(new JsonArray("Fabrikam")));
Assert.Equal<string?[]?>(["Contoso", "Fabrikam"], (string?[]?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam")));
Assert.Equal<string?[]?>(["value", "42", "true"], (string?[]?) new OpenIddictParameter(new JsonArray("value", 42, true)));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(JsonValue.Create("Fabrikam")));
Assert.Equal<IEnumerable<string?>?>(["false"], (StringValues?) new OpenIddictParameter(JsonValue.Create(false)));
Assert.Equal<IEnumerable<string?>?>(["42"], (StringValues?) new OpenIddictParameter(JsonValue.Create(42)));
Assert.Equal<IEnumerable<string?>?>(["42"], (StringValues?) new OpenIddictParameter(JsonValue.Create(42L)));
Assert.Equal<IEnumerable<string?>?>(["Fabrikam"], (StringValues?) new OpenIddictParameter(new JsonArray("Fabrikam")));
Assert.Equal<IEnumerable<string?>?>(["Contoso", "Fabrikam"], (StringValues?) new OpenIddictParameter(new JsonArray("Contoso", "Fabrikam")));
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.
*/
using System.Text.Json;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
using Xunit;
namespace OpenIddict.Abstractions.Tests.Primitives;
@ -48,7 +50,7 @@ public class OpenIddictRequestTests
{
/* property: */ nameof(OpenIddictRequest.Claims),
/* name: */ Parameters.Claims,
/* value: */ new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""userinfo"": {}}"))
/* value: */ new OpenIddictParameter(JsonObject.Parse(@"{""userinfo"": {}}"))
};
yield return new object[]
@ -216,7 +218,7 @@ public class OpenIddictRequestTests
{
/* property: */ nameof(OpenIddictRequest.Registration),
/* name: */ Parameters.Registration,
/* value: */ new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{""policy_uri"": ""http://www.fabrikam.com/policy""}"))
/* value: */ new OpenIddictParameter(JsonObject.Parse(@"{""policy_uri"": ""http://www.fabrikam.com/policy""}"))
};
yield return new object[]
@ -314,7 +316,16 @@ public class OpenIddictRequestTests
request.SetParameter(name, value);
// Act and assert
Assert.Equal(value.Value, typeof(OpenIddictRequest).GetProperty(property)!.GetValue(request));
var info = typeof(OpenIddictRequest).GetProperty(property)!;
if (typeof(JsonNode).IsAssignableFrom(info.PropertyType))
{
Assert.True(JsonNode.DeepEquals((JsonNode?) value.GetRawValue(), (JsonNode?) info.GetValue(request)));
}
else
{
Assert.Equal(value.GetRawValue(), info.GetValue(request));
}
}
[Theory]
@ -325,7 +336,16 @@ public class OpenIddictRequestTests
var request = new OpenIddictRequest();
// Act
typeof(OpenIddictRequest).GetProperty(property)!.SetValue(request, value.Value);
var info = typeof(OpenIddictRequest).GetProperty(property)!;
if (typeof(ImmutableArray<string?>).IsAssignableFrom(info.PropertyType))
{
info.SetValue(request, ImmutableCollectionsMarshal.AsImmutableArray((string?[]?) value.GetRawValue()));
}
else
{
info.SetValue(request, value.GetRawValue());
}
// Assert
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.
*/
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
using Xunit;
namespace OpenIddict.Abstractions.Tests.Primitives;
@ -145,7 +148,16 @@ public class OpenIddictResponseTests
response.SetParameter(name, value);
// Act and assert
Assert.Equal(value.Value, typeof(OpenIddictResponse).GetProperty(property)!.GetValue(response));
var info = typeof(OpenIddictResponse).GetProperty(property)!;
if (typeof(JsonNode).IsAssignableFrom(info.PropertyType))
{
Assert.True(JsonNode.DeepEquals((JsonNode?) value.GetRawValue(), (JsonNode?) info.GetValue(response)));
}
else
{
Assert.Equal(value.GetRawValue(), info.GetValue(response));
}
}
[Theory]
@ -156,7 +168,16 @@ public class OpenIddictResponseTests
var response = new OpenIddictResponse();
// Act
typeof(OpenIddictResponse).GetProperty(property)!.SetValue(response, value.Value);
var info = typeof(OpenIddictResponse).GetProperty(property)!;
if (typeof(ImmutableArray<string?>).IsAssignableFrom(info.PropertyType))
{
info.SetValue(response, ImmutableCollectionsMarshal.AsImmutableArray((string?[]?) value.GetRawValue()));
}
else
{
info.SetValue(response, value.GetRawValue());
}
// Assert
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.
*/
using System.Collections.Immutable;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Nodes;
@ -257,11 +258,11 @@ public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServ
Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal<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("value", (string?) response["object_parameter"]?["parameter"]);
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.Equal("value", (string?) response["node_object_parameter"]?["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("Bob l'Eponge", (string?) response["string_parameter"]);
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("value", (string?) response["object_parameter"]?["parameter"]);
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.Equal("value", (string?) response["node_object_parameter"]?["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)
.Select(group => new KeyValuePair<string, string?[]?>(
group.Key, group.Select(claim => claim.Value).ToArray()));
.Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json";
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.
*/
using System.Collections.Immutable;
using System.Net.Http;
using System.Net.Http.Json;
using AngleSharp.Html.Parser;
@ -245,17 +246,17 @@ public class OpenIddictServerIntegrationTestClient : IAsyncDisposable
{
message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!,
request.GetParameters().ToDictionary(
parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value)));
static parameter => parameter.Key,
static parameter => (StringValues) parameter.Value));
}
if (message.Method != HttpMethod.Get)
{
message.Content = new FormUrlEncodedContent(
from parameter in request.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
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");
context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -2541,7 +2537,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(response.ErrorUri);
Assert.NotNull(response.AccessToken);
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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]
@ -2660,11 +2656,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context =>
{
context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -2683,7 +2675,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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]
@ -5204,11 +5196,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context =>
{
context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -5231,7 +5219,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(response.ErrorUri);
Assert.NotNull(response.RequestUri);
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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]
@ -5283,11 +5271,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context =>
{
context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -5306,6 +5290,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -1051,7 +1047,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Null(response.ErrorUri);
Assert.NotNull(response.DeviceCode);
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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]
@ -1116,11 +1112,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context =>
{
context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -1136,7 +1128,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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]
@ -1467,11 +1459,7 @@ public abstract partial class OpenIddictServerIntegrationTests
.SetClaim(Claims.Subject, "Bob le Magnifique");
context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Parameters["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -1496,7 +1484,7 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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]

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

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

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

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

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

@ -387,7 +387,7 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Equal(3, response.Count);
Assert.Equal("http://localhost/", (string?) response[Claims.Issuer]);
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal<string?[]?>(["Fabrikam", "Contoso"], (string?[]?) response[Claims.Audience]);
Assert.Equal<IEnumerable<string?>?>(["Fabrikam", "Contoso"], (ImmutableArray<string?>?) response[Claims.Audience]);
}
[Fact]
@ -788,11 +788,7 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.UseInlineHandler(context =>
{
context.Response["custom_parameter"] = "custom_value";
context.Response["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
context.Response["parameter_with_multiple_values"] = new(["custom_value_1", "custom_value_2"]);
return default;
}));
@ -808,6 +804,6 @@ public abstract partial class OpenIddictServerIntegrationTests
// Assert
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal<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("Bob l'Eponge", (string?) response["string_parameter"]);
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("value", (string?) response["object_parameter"]?["parameter"]);
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.Equal("value", (string?) response["node_object_parameter"]?["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("Bob l'Eponge", (string?) response["string_parameter"]);
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("value", (string?) response["object_parameter"]?["parameter"]);
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.Equal("value", (string?) response["node_object_parameter"]?["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.
*/
using System.Collections.Immutable;
using System.Security.Claims;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
@ -254,7 +255,7 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["json_parameter"]);
Assert.Equal(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["json_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["json_parameter"]).ValueKind);
}
@ -479,7 +480,7 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
Assert.Equal(JsonValueKind.Number, ((JsonElement) response["integer_parameter"]).ValueKind);
Assert.Equal("Bob l'Eponge", (string?) response["string_parameter"]);
Assert.Equal(JsonValueKind.String, ((JsonElement) response["string_parameter"]).ValueKind);
Assert.Equal(["Contoso", "Fabrikam"], (string?[]?) response["json_parameter"]);
Assert.Equal(["Contoso", "Fabrikam"], (ImmutableArray<string?>?) response["json_parameter"]);
Assert.Equal(JsonValueKind.Array, ((JsonElement) response["json_parameter"]).ValueKind);
}
@ -697,8 +698,8 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
}
var claims = result.Identity.Claims.GroupBy(claim => claim.Type)
.Select(group => new KeyValuePair<string, string?[]?>(
group.Key, group.Select(claim => claim.Value).ToArray()));
.Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json";
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.
*/
using System.Collections.Immutable;
using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Authentication;
@ -184,8 +185,8 @@ public partial class OpenIddictValidationAspNetCoreIntegrationTests : OpenIddict
}
var claims = result.Principal.Claims.GroupBy(claim => claim.Type)
.Select(group => new KeyValuePair<string, string?[]?>(
group.Key, group.Select(claim => claim.Value).ToArray()));
.Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json";
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.
*/
using System.Collections.Immutable;
using System.Net.Http;
using System.Net.Http.Json;
using System.Runtime.InteropServices;
using AngleSharp.Html.Parser;
using Microsoft.Extensions.Primitives;
using OpenIddict.Extensions;
@ -245,17 +247,17 @@ public class OpenIddictValidationIntegrationTestClient : IAsyncDisposable
{
message.RequestUri = OpenIddictHelpers.AddQueryStringParameters(message.RequestUri!,
request.GetParameters().ToDictionary(
parameter => parameter.Key,
parameter => new StringValues((string?[]?) parameter.Value)));
static parameter => parameter.Key,
static parameter => (StringValues) parameter.Value));
}
if (message.Method != HttpMethod.Get)
{
message.Content = new FormUrlEncodedContent(
from parameter in request.GetParameters()
let values = (string?[]?) parameter.Value
let values = (ImmutableArray<string?>?) parameter.Value
where values is not null
from value in values
from value in values.GetValueOrDefault()
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.
*/
using System.Collections.Immutable;
using System.Security.Claims;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
@ -169,8 +170,8 @@ public partial class OpenIddictValidationOwinIntegrationTests : OpenIddictValida
}
var claims = result.Identity.Claims.GroupBy(claim => claim.Type)
.Select(group => new KeyValuePair<string, string?[]?>(
group.Key, group.Select(claim => claim.Value).ToArray()));
.Select(group => new KeyValuePair<string, ImmutableArray<string?>?>(
group.Key, group.Select(claim => claim.Value).ToImmutableArray<string?>()));
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new OpenIddictResponse(claims)));

Loading…
Cancel
Save