diff --git a/eng/Versions.props b/eng/Versions.props
index 12070c3f..8d861cc5 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -30,12 +30,11 @@
1.0.0
+ 1.0.0
1.8.5
4.4.0
6.3.0
2019.1.3
- 12.0.2
- 1.0.2
5.6.0
1.5.0
4.0.0
@@ -43,6 +42,7 @@
4.7.63
5.2.2
4.0.0
+ 4.6.0
4.5.3
diff --git a/samples/Mvc.Server/Controllers/UserinfoController.cs b/samples/Mvc.Server/Controllers/UserinfoController.cs
index c6ef839b..acdbe83b 100644
--- a/samples/Mvc.Server/Controllers/UserinfoController.cs
+++ b/samples/Mvc.Server/Controllers/UserinfoController.cs
@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Mvc.Server.Models;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
@@ -51,7 +50,7 @@ namespace Mvc.Server.Controllers
if (User.HasClaim(OpenIddictConstants.Claims.Scope, "roles"))
{
- claims["roles"] = JArray.FromObject(await _userManager.GetRolesAsync(user));
+ claims["roles"] = await _userManager.GetRolesAsync(user);
}
// Note: the complete list of standard claims supported by the OpenID Connect specification
diff --git a/src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj b/src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj
index 6ea13d67..97b0faeb 100644
--- a/src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj
+++ b/src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj
@@ -12,13 +12,14 @@
-
+
+
diff --git a/src/OpenIddict.Abstractions/OpenIddictConstants.cs b/src/OpenIddict.Abstractions/OpenIddictConstants.cs
index c9e6af1a..2331e716 100644
--- a/src/OpenIddict.Abstractions/OpenIddictConstants.cs
+++ b/src/OpenIddict.Abstractions/OpenIddictConstants.cs
@@ -178,6 +178,20 @@ namespace OpenIddict.Abstractions
public const string RefreshToken = "refresh_token";
}
+ public static class JsonWebTokenTypes
+ {
+ public const string AccessToken = "at+jwt";
+ public const string IdentityToken = "jwt";
+
+ public static class Private
+ {
+ public const string AuthorizationCode = "oi_auc+jwt";
+ public const string DeviceCode = "oi_dvc+jwt";
+ public const string RefreshToken = "oi_reft+jwt";
+ public const string UserCode = "oi_usrc+jwt";
+ }
+ }
+
public static class Metadata
{
public const string AcrValuesSupported = "acr_values_supported";
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs
index cafbb874..9963caf6 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs
@@ -5,16 +5,16 @@
*/
using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace OpenIddict.Abstractions
{
///
/// Represents a JSON.NET converter able to convert OpenIddict primitives.
///
- public class OpenIddictConverter : JsonConverter
+ public class OpenIddictConverter : JsonConverter
{
///
/// Determines whether the specified type is supported by this converter.
@@ -36,62 +36,50 @@ namespace OpenIddict.Abstractions
///
/// The JSON reader.
/// The type of the deserialized instance.
- /// The existing , if applicable.
- /// The JSON serializer.
+ /// The JSON serializer options.
/// The deserialized instance.
- public override object ReadJson(
- [NotNull] JsonReader reader, [NotNull] Type type,
- [CanBeNull] object value, [CanBeNull] JsonSerializer serializer)
+ public override OpenIddictMessage Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
{
- if (reader == null)
- {
- throw new ArgumentNullException(nameof(reader));
- }
-
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
// Note: OpenIddict primitives are always represented as JSON objects.
- var payload = JToken.Load(reader) as JObject;
- if (payload == null)
+ var payload = JsonSerializer.Deserialize(ref reader, options);
+ if (payload.ValueKind != JsonValueKind.Object)
{
- throw new JsonSerializationException("An error occurred while reading the JSON payload.");
+ throw new JsonException("An error occurred while reading the JSON payload.");
}
- // If no existing value was specified, instantiate a
- // new request/response depending on the requested type.
- var message = value as OpenIddictMessage;
- if (message == null)
+ OpenIddictMessage message;
+
+ if (type == typeof(OpenIddictMessage))
{
- if (type == typeof(OpenIddictMessage))
- {
- message = new OpenIddictMessage();
- }
+ message = new OpenIddictMessage();
+ }
- else if (type == typeof(OpenIddictRequest))
- {
- message = new OpenIddictRequest();
- }
+ else if (type == typeof(OpenIddictRequest))
+ {
+ message = new OpenIddictRequest();
+ }
- else if (type == typeof(OpenIddictResponse))
- {
- message = new OpenIddictResponse();
- }
+ else if (type == typeof(OpenIddictResponse))
+ {
+ message = new OpenIddictResponse();
}
- if (message != null)
+ else
{
- foreach (var parameter in payload.Properties())
- {
- message.AddParameter(parameter.Name, parameter.Value);
- }
+ throw new ArgumentException("The specified type is not supported.", nameof(type));
+ }
- return message;
+ foreach (var parameter in payload.EnumerateObject())
+ {
+ message.AddParameter(parameter.Name, parameter.Value);
}
- throw new ArgumentException("The specified type is not supported.", nameof(type));
+ return message;
}
///
@@ -99,8 +87,8 @@ namespace OpenIddict.Abstractions
///
/// The JSON writer.
/// The instance.
- /// The JSON serializer.
- public override void WriteJson([NotNull] JsonWriter writer, [NotNull] object value, [CanBeNull] JsonSerializer serializer)
+ /// The JSON serializer options.
+ public override void Write(Utf8JsonWriter writer, OpenIddictMessage value, JsonSerializerOptions options)
{
if (writer == null)
{
@@ -112,31 +100,24 @@ namespace OpenIddict.Abstractions
throw new ArgumentNullException(nameof(value));
}
- if (value is OpenIddictMessage message)
+ writer.WriteStartObject();
+
+ foreach (var parameter in value.GetParameters())
{
- writer.WriteStartObject();
+ writer.WritePropertyName(parameter.Key);
- foreach (var parameter in message.GetParameters())
+ var token = (JsonElement?) parameter.Value;
+ if (token == null)
{
- writer.WritePropertyName(parameter.Key);
-
- var token = (JToken) parameter.Value;
- if (token == null)
- {
- writer.WriteNull();
+ writer.WriteNullValue();
- continue;
- }
-
- token.WriteTo(writer);
+ continue;
}
- writer.WriteEndObject();
-
- return;
+ token.Value.WriteTo(writer);
}
- throw new ArgumentException("The specified object is not supported.", nameof(value));
+ writer.WriteEndObject();
}
}
}
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
index ce3e2bec..3f751541 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
@@ -11,10 +11,10 @@ using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using JetBrains.Annotations;
using Microsoft.Extensions.Primitives;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Abstractions
@@ -538,7 +538,9 @@ namespace OpenIddict.Abstractions
return ImmutableArray.Create();
}
- return JArray.Parse(destinations).Values().Distinct(StringComparer.OrdinalIgnoreCase).ToImmutableArray();
+ return JsonSerializer.Deserialize>(destinations)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToImmutableArray();
}
///
@@ -565,7 +567,8 @@ namespace OpenIddict.Abstractions
return false;
}
- return JArray.Parse(destinations).Values().Contains(destination, StringComparer.OrdinalIgnoreCase);
+ return JsonSerializer.Deserialize>(destinations)
+ .Contains(destination, StringComparer.OrdinalIgnoreCase);
}
///
@@ -593,7 +596,11 @@ namespace OpenIddict.Abstractions
}
claim.Properties[Properties.Destinations] =
- new JArray(destinations.Distinct(StringComparer.OrdinalIgnoreCase)).ToString(Formatting.None);
+ JsonSerializer.Serialize(destinations.Distinct(StringComparer.OrdinalIgnoreCase), new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return claim;
}
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs
index 3badeb5a..ab969867 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs
@@ -7,13 +7,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Globalization;
using System.IO;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
using Microsoft.Extensions.Primitives;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace OpenIddict.Abstractions
{
@@ -38,7 +38,7 @@ namespace OpenIddict.Abstractions
/// Initializes a new OpenIddict message.
///
/// The message parameters.
- public OpenIddictMessage([NotNull] IEnumerable> parameters)
+ public OpenIddictMessage([NotNull] IEnumerable> parameters)
{
if (parameters == null)
{
@@ -316,12 +316,12 @@ namespace OpenIddict.Abstractions
/// The indented JSON representation corresponding to this message.
public override string ToString()
{
- var builder = new StringBuilder();
-
- using var writer = new JsonTextWriter(new StringWriter(builder, CultureInfo.InvariantCulture))
+ using var stream = new MemoryStream();
+ using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions
{
- Formatting = Formatting.Indented
- };
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ Indented = true
+ });
writer.WriteStartObject();
@@ -343,26 +343,27 @@ namespace OpenIddict.Abstractions
case OpenIddictConstants.Parameters.RefreshToken:
case OpenIddictConstants.Parameters.Token:
{
- writer.WriteValue("[removed for security reasons]");
+ writer.WriteStringValue("[removed for security reasons]");
continue;
}
}
- var token = (JToken) parameter.Value;
+ var token = (JsonElement?) parameter.Value;
if (token == null)
{
- writer.WriteNull();
+ writer.WriteNullValue();
continue;
}
- token.WriteTo(writer);
+ token.Value.WriteTo(writer);
}
writer.WriteEndObject();
+ writer.Flush();
- return builder.ToString();
+ return Encoding.UTF8.GetString(stream.ToArray());
}
}
}
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
index bd1e8361..9ad8661e 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
@@ -7,15 +7,16 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Globalization;
using System.Linq;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace OpenIddict.Abstractions
{
///
- /// Represents an OpenID Connect parameter value, that can be either a primitive value,
+ /// Represents an OpenIddict parameter value, that can be either a primitive value,
/// an array of strings or a complex JSON representation containing child nodes.
///
public readonly struct OpenIddictParameter : IEquatable
@@ -39,7 +40,14 @@ namespace OpenIddict.Abstractions
/// parameter using the specified value.
///
/// The parameter value.
- public OpenIddictParameter(JToken value) => Value = value;
+ public OpenIddictParameter(JsonElement value) => Value = value;
+
+ ///
+ /// Initializes a new OpenID Connect
+ /// parameter using the specified value.
+ ///
+ /// The parameter value.
+ public OpenIddictParameter(JsonElement? value) => Value = value;
///
/// Initializes a new OpenID Connect
@@ -96,33 +104,119 @@ namespace OpenIddict.Abstractions
///
/// The other object to which to compare this instance.
/// true if the two instances are equal, false otherwise.
- public bool Equals(OpenIddictParameter parameter) => Value switch
+ public bool Equals(OpenIddictParameter parameter)
{
- // If the two parameters reference the same instance, return true.
- // Note: true will also be returned if the two parameters are null.
- var value when ReferenceEquals(value, parameter.Value) => true,
-
- // If one of the two parameters is null, return false.
- null => false,
- var _ when parameter.Value == null => false,
-
- // If the two parameters are string arrays, use SequenceEqual().
- string[] array when parameter.Value is string[] other => array.SequenceEqual(other),
-
- // If the two parameters are JSON values, use JToken.DeepEquals().
- JToken token when parameter.Value is JToken other => JToken.DeepEquals(token, other),
-
- // If the current instance is a JValue, compare the
- // underlying value to the other parameter value.
- JValue value => value.Value != null && value.Value.Equals(parameter.Value),
-
- // If the other parameter is a JValue, compare the
- // underlying value to the current parameter value.
- var value when parameter.Value is JValue other => other.Value != null && other.Value.Equals(value),
-
- // Otherwise, directly compare the two underlying values.
- _ => Value.Equals(parameter.Value)
- };
+ return Value switch
+ {
+ // If the two parameters reference the same instance, return true.
+ // Note: true will also be returned if the two parameters are null.
+ var value when ReferenceEquals(value, parameter.Value) => true,
+
+ // If one of the two parameters is null, return false.
+ null => false,
+ var _ when parameter.Value == null => false,
+
+ // If the two parameters are string arrays, use SequenceEqual().
+ string[] value when parameter.Value is string[] array => value.SequenceEqual(array),
+
+ // If the two parameters are JsonElement instances, use the custom comparer.
+ JsonElement value when parameter.Value is JsonElement element => Equals(value, element),
+
+ // When one of the parameters is a bool, compare them as booleans.
+ JsonElement value when value.ValueKind == JsonValueKind.True
+ && parameter.Value is bool boolean => boolean,
+ JsonElement value when value.ValueKind == JsonValueKind.False
+ && parameter.Value is bool boolean => !boolean,
+
+ bool value when parameter.Value is JsonElement element
+ && element.ValueKind == JsonValueKind.True => value,
+ bool value when parameter.Value is JsonElement element
+ && element.ValueKind == JsonValueKind.False => !value,
+
+ // When one of the parameters is a number, compare them as integers.
+ JsonElement value when value.ValueKind == JsonValueKind.Number
+ && parameter.Value is long integer
+ => integer == value.GetInt64(),
+
+ long value when parameter.Value is JsonElement element
+ && element.ValueKind == JsonValueKind.Number
+ => value == element.GetInt64(),
+
+ // When one of the parameters is a string, compare them as texts.
+ JsonElement value when value.ValueKind == JsonValueKind.String
+ && parameter.Value is string text
+ => string.Equals(value.GetString(), text, StringComparison.Ordinal),
+
+ string value when parameter.Value is JsonElement element
+ && element.ValueKind == JsonValueKind.String
+ => string.Equals(value, element.GetString(), StringComparison.Ordinal),
+
+ // Otherwise, use direct CLR comparison.
+ var value => value.Equals(parameter.Value)
+ };
+
+ static bool Equals(JsonElement left, JsonElement right)
+ {
+ switch (left.ValueKind)
+ {
+ case JsonValueKind.Undefined:
+ return right.ValueKind == JsonValueKind.Undefined;
+
+ case JsonValueKind.Null:
+ return right.ValueKind == JsonValueKind.Null;
+
+ case JsonValueKind.False:
+ return right.ValueKind == JsonValueKind.False;
+
+ case JsonValueKind.True:
+ return right.ValueKind == JsonValueKind.True;
+
+ case JsonValueKind.Number when right.ValueKind == JsonValueKind.Number:
+ return left.GetInt64() == right.GetInt64();
+
+ case JsonValueKind.String when right.ValueKind == JsonValueKind.String:
+ return string.Equals(left.GetString(), right.GetString(), StringComparison.Ordinal);
+
+ case JsonValueKind.Array when right.ValueKind == JsonValueKind.Array:
+ if (left.GetArrayLength() != right.GetArrayLength())
+ {
+ return false;
+ }
+
+ using (var enumerator = left.EnumerateArray())
+ {
+ for (var index = 0; enumerator.MoveNext(); index++)
+ {
+ if (!Equals(left[index], right[index]))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+
+ case JsonValueKind.Object when right.ValueKind == JsonValueKind.Object:
+ foreach (var property in left.EnumerateObject())
+ {
+ if (!right.TryGetProperty(property.Name, out JsonElement element) ||
+ property.Value.ValueKind != element.ValueKind)
+ {
+ return false;
+ }
+
+ if (!Equals(property.Value, element))
+ {
+ return false;
+ }
+ }
+
+ return true;
+
+ default: return false;
+ }
+ }
+ }
///
/// Determines whether the current
@@ -137,9 +231,69 @@ namespace OpenIddict.Abstractions
/// Returns the hash code of the current instance.
///
/// The hash code for the current instance.
- // Note: if the value is a JValue, JSON.NET will automatically
- // return the hash code corresponding to the underlying value.
- public override int GetHashCode() => Value?.GetHashCode() ?? 0;
+ public override int GetHashCode()
+ {
+ return Value switch
+ {
+ // When the parameter value is null, return 0.
+ null => 0,
+
+ // When the parameter is a JsonElement, compute its hash code.
+ JsonElement value => GetHashCode(value),
+
+ // Otherwise, use the default hash code method.
+ var value => value.GetHashCode()
+ };
+
+ static int GetHashCode(JsonElement value)
+ {
+ switch (value.ValueKind)
+ {
+ case JsonValueKind.Undefined:
+ case JsonValueKind.Null:
+ return 0;
+
+ case JsonValueKind.False:
+ return false.GetHashCode();
+
+ case JsonValueKind.True:
+ return true.GetHashCode();
+
+ case JsonValueKind.Number:
+ return value.GetInt64().GetHashCode();
+
+ case JsonValueKind.String:
+ return value.GetString().GetHashCode();
+
+ case JsonValueKind.Array:
+ {
+ var hash = new HashCode();
+
+ foreach (var element in value.EnumerateArray())
+ {
+ hash.Add(GetHashCode(element));
+ }
+
+ return hash.ToHashCode();
+ }
+
+ case JsonValueKind.Object:
+ {
+ var hash = new HashCode();
+
+ foreach (var property in value.EnumerateObject())
+ {
+ hash.Add(property.Name);
+ hash.Add(GetHashCode(property.Value));
+ }
+
+ return hash.ToHashCode();
+ }
+
+ default: return 0;
+ }
+ }
+ }
///
/// Gets the child item corresponding to the specified index.
@@ -165,26 +319,19 @@ namespace OpenIddict.Abstractions
return new OpenIddictParameter(array[index]);
}
- // If the value is not a JSON array, return null.
- if (Value is JArray token)
+ if (Value is JsonElement element && element.ValueKind == JsonValueKind.Array)
{
// If the specified index goes beyond the
// number of items in the array, return null.
- if (index >= token.Count)
+ if (index >= element.GetArrayLength())
{
return null;
}
- // If the item doesn't exist, return a null parameter.
- var value = token[index];
- if (value == null)
- {
- return null;
- }
-
- return new OpenIddictParameter(value);
+ return new OpenIddictParameter(element[index]);
}
+ // If the value is not a JSON array, return null.
return null;
}
@@ -200,16 +347,15 @@ namespace OpenIddict.Abstractions
throw new ArgumentException("The item name cannot be null or empty.", nameof(name));
}
- if (Value is JObject dictionary)
+ if (Value is JsonElement element && element.ValueKind == JsonValueKind.Object)
{
- // If the item doesn't exist, return a null parameter.
- var value = dictionary[name];
- if (value == null)
+ if (element.TryGetProperty(name, out JsonElement value) && value.ValueKind != JsonValueKind.Null)
{
- return null;
+ return new OpenIddictParameter(value);
}
- return new OpenIddictParameter(value);
+ // If the item doesn't exist, return a null parameter.
+ return null;
}
return null;
@@ -229,18 +375,25 @@ namespace OpenIddict.Abstractions
}
}
- if (Value is JToken token)
+ if (Value is JsonElement element)
{
- foreach (var child in token.Children())
+ switch (element.ValueKind)
{
- if (!(child is JProperty property))
- {
- yield return new KeyValuePair(null, child);
+ case JsonValueKind.Array:
+ foreach (var value in element.EnumerateArray())
+ {
+ yield return new KeyValuePair(null, value);
+ }
- continue;
- }
+ break;
+
+ case JsonValueKind.Object:
+ foreach (var property in element.EnumerateObject())
+ {
+ yield return new KeyValuePair(property.Name, property.Value);
+ }
- yield return new KeyValuePair(property.Name, property.Value);
+ break;
}
}
@@ -254,12 +407,17 @@ namespace OpenIddict.Abstractions
public override string ToString() => Value switch
{
null => string.Empty,
- JValue value when value.Value == null => string.Empty,
- string[] array => string.Join(", ", array),
+ string value => value,
+ string[] value => string.Join(", ", value),
- JValue value => value.Value.ToString(),
- JToken token => token.ToString(Formatting.None),
+ JsonElement value when value.ValueKind == JsonValueKind.Undefined => string.Empty,
+ JsonElement value when value.ValueKind == JsonValueKind.Null => string.Empty,
+
+ JsonElement value when value.ValueKind != JsonValueKind.Array &&
+ value.ValueKind != JsonValueKind.Object => value.GetString(),
+
+ JsonElement value => value.ToString(),
_ => Value.ToString()
};
@@ -277,9 +435,10 @@ namespace OpenIddict.Abstractions
throw new ArgumentException("The parameter name cannot be null or empty.", nameof(name));
}
- if (Value is JObject dictionary && dictionary.TryGetValue(name, out JToken token))
+ if (Value is JsonElement element && element.ValueKind == JsonValueKind.Object &&
+ element.TryGetProperty(name, out JsonElement property))
{
- value = new OpenIddictParameter(token);
+ value = new OpenIddictParameter(property);
return true;
}
@@ -310,70 +469,234 @@ namespace OpenIddict.Abstractions
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator bool(OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator bool(OpenIddictParameter? parameter)
+ => ((bool?) parameter).GetValueOrDefault();
///
/// Converts an instance to a nullable boolean.
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator bool?(OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator bool?(OpenIddictParameter? parameter) => parameter?.Value switch
+ {
+ // When the parameter is a null value, return null.
+ null => null,
- ///
- /// Converts an instance to a .
- ///
- /// The parameter to convert.
- /// The converted value.
- public static explicit operator JArray(OpenIddictParameter? parameter) => Convert(parameter);
+ // When the parameter is a boolean value, return it as-is.
+ bool value => value,
- ///
- /// Converts an instance to a .
- ///
- /// The parameter to convert.
- /// The converted value.
- public static explicit operator JObject(OpenIddictParameter? parameter) => Convert(parameter);
+ // When the parameter is a string value, try to parse it.
+ string value => bool.TryParse(value, out var result) ? (bool?) result : null,
+
+ // When the parameter is a JsonElement representing null, return null.
+ JsonElement value when value.ValueKind == JsonValueKind.Undefined => null,
+ JsonElement value when value.ValueKind == JsonValueKind.Null => null,
+
+ // When the parameter is a JsonElement representing a boolean, return it as-is.
+ JsonElement value when value.ValueKind == JsonValueKind.False => false,
+ JsonElement value when value.ValueKind == JsonValueKind.True => true,
+
+ // When the parameter is a JsonElement representing a string, try to parse it.
+ JsonElement value when value.ValueKind == JsonValueKind.String
+ => bool.TryParse(value.GetString(), out var result) ? (bool?) result : null,
+
+ // If the parameter is of a different type, return null to indicate the conversion failed.
+ _ => null
+ };
///
- /// Converts an instance to a .
+ /// Converts an instance to a .
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator JToken(OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator JsonElement(OpenIddictParameter? parameter)
+ => ((JsonElement?) parameter).GetValueOrDefault();
///
- /// Converts an instance to a .
+ /// Converts an instance to a nullale .
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator JValue(OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator JsonElement?(OpenIddictParameter? parameter)
+ {
+ return parameter?.Value switch
+ {
+ // When the parameter is a null value, return null.
+ null => default(JsonElement?),
+
+ // When the parameter is a JsonElement representing null, return null.
+ JsonElement value when value.ValueKind == JsonValueKind.Undefined => null,
+ JsonElement value when value.ValueKind == JsonValueKind.Null => null,
+
+ // When the parameter is already a JsonElement, return it as-is.
+ JsonElement value => value,
+
+ // When the parameter is a string, try to derialize it to get a JsonElement instance.
+ string value => DeserializeElement(value) ?? DeserializeElement(JsonSerializer.Serialize(value, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ })),
+
+ // Otherwise, serialize it to get a JsonElement instance.
+ var value => DeserializeElement(JsonSerializer.Serialize(value, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ }))
+ };
+
+ static JsonElement? DeserializeElement(string value)
+ {
+ try { return JsonSerializer.Deserialize(value); }
+ catch (JsonException) { return null; }
+ }
+ }
///
/// Converts an instance to a long integer.
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator long(OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator long(OpenIddictParameter? parameter)
+ => ((long?) parameter).GetValueOrDefault();
///
/// Converts an instance to a nullable long integer.
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator long?(OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator long?(OpenIddictParameter? parameter) => parameter?.Value switch
+ {
+ // When the parameter is a null value, return null.
+ null => null,
+
+ // When the parameter is an integer, return it as-is.
+ long value => value,
+
+ // When the parameter is a string value, try to parse it.
+ string value
+ => long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) ? (long?) result : null,
+
+ // When the parameter is a JsonElement representing null, return null.
+ JsonElement value when value.ValueKind == JsonValueKind.Undefined => null,
+ JsonElement value when value.ValueKind == JsonValueKind.Null => null,
+
+ // When the parameter is a JsonElement representing a number, return it as-is.
+ JsonElement value when value.ValueKind == JsonValueKind.Number
+ => value.TryGetInt64(out var result) ? (long?) result : null,
+
+ // When the parameter is a JsonElement representing a string, try to parse it.
+ JsonElement value when value.ValueKind == JsonValueKind.String
+ => long.TryParse(value.GetString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) ? (long?) result : null,
+
+ // If the parameter is of a different type, return null to indicate the conversion failed.
+ _ => null
+ };
///
/// Converts an instance to a string.
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator string(OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator string(OpenIddictParameter? parameter) => parameter?.Value switch
+ {
+ // When the parameter is a null value, return null.
+ null => null,
+
+ // When the parameter is a string value, return it as-is.
+ string value => value,
+
+ // When the parameter is a boolean value, use its string representation.
+ bool value => value.ToString(),
+
+ // When the parameter is an integer, use its string representation.
+ long value => value.ToString(CultureInfo.InvariantCulture),
+
+ // When the parameter is a JsonElement representing null, return null.
+ JsonElement value when value.ValueKind == JsonValueKind.Undefined => null,
+ JsonElement value when value.ValueKind == JsonValueKind.Null => null,
+
+ // When the parameter is a JsonElement representing a string, return it as-is.
+ JsonElement value when value.ValueKind == JsonValueKind.String => value.GetString(),
+
+ // When the parameter is a JsonElement that doesn't represent a string, serialize it.
+ JsonElement value => JsonSerializer.Serialize(value, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ }),
+
+ // If the parameter is of a different type, return null to indicate the conversion failed.
+ _ => null
+ };
///
/// Converts an instance to an array of strings.
///
/// The parameter to convert.
/// The converted value.
- public static explicit operator string[](OpenIddictParameter? parameter) => Convert(parameter);
+ public static explicit operator string[](OpenIddictParameter? parameter)
+ {
+ return parameter?.Value switch
+ {
+ // When the parameter is a null value, return a null array.
+ null => null,
+
+ // When the parameter is already an array of strings, return it as-is.
+ string[] value => value,
+
+ // When the parameter is a string value, return an array with a single entry.
+ string value => new string[] { value },
+
+ // When the parameter is a boolean value, return an array with its string representation.
+ bool value => new string[] { value.ToString() },
+
+ // When the parameter is an integer, return an array with its string representation.
+ long value => new string[] { value.ToString(CultureInfo.InvariantCulture) },
+
+ // When the parameter is a JsonElement representing null, return null.
+ JsonElement value when value.ValueKind == JsonValueKind.Undefined => null,
+ JsonElement value when value.ValueKind == JsonValueKind.Null => null,
+
+ // When the parameter is a JsonElement representing a string, return an array with a single entry.
+ JsonElement value when value.ValueKind == JsonValueKind.String
+ => new string[] { value.GetString() },
+
+ // When the parameter is a JsonElement representing a number, return an array with a single entry.
+ JsonElement value when value.ValueKind == JsonValueKind.Number
+ => new string[] { value.GetInt64().ToString(CultureInfo.InvariantCulture) },
+
+ // When the parameter is a JsonElement representing a boolean, return an array with a single entry.
+ JsonElement value when value.ValueKind == JsonValueKind.False => new string[] { bool.FalseString },
+ JsonElement value when value.ValueKind == JsonValueKind.True => new string[] { bool.TrueString },
+
+ // When the parameter is a JsonElement representing an array of strings, return it.
+ JsonElement value when value.ValueKind == JsonValueKind.Array => CreateArray(value),
+
+ // If the parameter is of a different type, return null to indicate the conversion failed.
+ _ => null
+ };
+
+ static string[] CreateArray(JsonElement value)
+ {
+ var array = new string[value.GetArrayLength()];
+ using var enumerator = value.EnumerateArray();
+
+ for (var index = 0; enumerator.MoveNext(); index++)
+ {
+ var element = enumerator.Current;
+ if (element.ValueKind != JsonValueKind.String)
+ {
+ return null;
+ }
+
+ array[index] = element.GetString();
+ }
+
+ return array;
+ }
+ }
///
/// Converts a boolean to an instance.
@@ -390,11 +713,11 @@ namespace OpenIddict.Abstractions
public static implicit operator OpenIddictParameter(bool? value) => new OpenIddictParameter(value);
///
- /// Converts a to an instance.
+ /// Converts a to an instance.
///
/// The value to convert
/// An instance.
- public static implicit operator OpenIddictParameter(JToken value) => new OpenIddictParameter(value);
+ public static implicit operator OpenIddictParameter(JsonElement value) => new OpenIddictParameter(value);
///
/// Converts a long integer to an instance.
@@ -425,78 +748,36 @@ namespace OpenIddict.Abstractions
public static implicit operator OpenIddictParameter(string[] value) => new OpenIddictParameter(value);
///
- /// Converts the parameter to the specified generic type.
+ /// Determines whether an OpenID Connect parameter is null or empty.
///
- /// The type the parameter will be converted to.
- /// The instance.
- /// The converted parameter.
- private static T Convert(OpenIddictParameter? parameter)
+ /// The OpenID Connect parameter.
+ /// true if the parameter is null or empty, false otherwise.
+ public static bool IsNullOrEmpty(OpenIddictParameter parameter)
{
- try
+ return parameter.Value switch
{
- return parameter?.Value switch
- {
- null => default,
+ null => true,
- T value => value,
+ string value => string.IsNullOrEmpty(value),
+ string[] value => value.Length == 0,
- string value when typeof(T) == typeof(string[]) => (T) (object) new string[] { value },
+ JsonElement value when value.ValueKind == JsonValueKind.Undefined => true,
+ JsonElement value when value.ValueKind == JsonValueKind.Null => true,
- // Note: when the parameter is represented as a string, try to
- // deserialize it if the requested type is a JArray or a JObject.
- string value when typeof(T) == typeof(JArray) => (T) (object) JArray.Parse(value),
+ JsonElement value when value.ValueKind == JsonValueKind.String
+ => string.IsNullOrEmpty(value.GetString()),
- string value when typeof(T) == typeof(JObject) => (T) (object) JObject.Parse(value),
+ JsonElement value when value.ValueKind == JsonValueKind.Array => value.GetArrayLength() == 0,
+ JsonElement value when value.ValueKind == JsonValueKind.Object => IsEmptyNode(value),
- string[] array => new JArray(array).ToObject(),
-
- JValue value when typeof(T) == typeof(string[]) => (T) (object) new string[]
- {
- value.ToObject()
- },
+ _ => false
+ };
- JToken token => token.ToObject(),
-
- var value when typeof(T) == typeof(string[]) => (T) (object) new string[]
- {
- new JValue(value).ToObject()
- },
-
- _ => new JValue(parameter?.Value).ToObject()
- };
- }
-
- // Swallow the conversion exceptions thrown by JSON.NET.
- catch (Exception exception) when (exception is ArgumentException ||
- exception is FormatException ||
- exception is InvalidCastException ||
- exception is JsonReaderException ||
- exception is JsonSerializationException)
+ static bool IsEmptyNode(JsonElement value)
{
- return default;
+ using var enumerator = value.EnumerateObject();
+ return !enumerator.MoveNext();
}
-
- // Other exceptions will be automatically re-thrown.
}
-
- ///
- /// Determines whether an OpenID Connect parameter is null or empty.
- ///
- /// The OpenID Connect parameter.
- /// true if the parameter is null or empty, false otherwise.
- public static bool IsNullOrEmpty(OpenIddictParameter parameter) => parameter.Value switch
- {
- null => true,
-
- string value => string.IsNullOrEmpty(value),
-
- string[] array => array.Length == 0,
-
- JValue value when value.Value is string text => string.IsNullOrEmpty(text),
- JArray array => !array.HasValues,
- JToken token => !token.HasValues,
-
- _ => false
- };
}
}
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs
index a953ce6a..f2e2cd2a 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs
@@ -6,10 +6,10 @@
using System.Collections.Generic;
using System.Diagnostics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
using Microsoft.Extensions.Primitives;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace OpenIddict.Abstractions
{
@@ -35,7 +35,7 @@ namespace OpenIddict.Abstractions
/// Initializes a new OpenIddict request.
///
/// The request parameters.
- public OpenIddictRequest([NotNull] IEnumerable> parameters)
+ public OpenIddictRequest([NotNull] IEnumerable> parameters)
: base(parameters) { }
///
@@ -105,9 +105,9 @@ namespace OpenIddict.Abstractions
///
/// Gets or sets the "claims" parameter.
///
- public JObject Claims
+ public JsonElement Claims
{
- get => (JObject) GetParameter(OpenIddictConstants.Parameters.Claims);
+ get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Claims);
set => SetParameter(OpenIddictConstants.Parameters.Claims, value);
}
@@ -402,9 +402,9 @@ namespace OpenIddict.Abstractions
///
/// Gets or sets the "registration" parameter.
///
- public JObject Registration
+ public JsonElement Registration
{
- get => (JObject) GetParameter(OpenIddictConstants.Parameters.Registration);
+ get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Registration);
set => SetParameter(OpenIddictConstants.Parameters.Registration, value);
}
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs
index d31f5b37..50f79c3e 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs
@@ -6,10 +6,10 @@
using System.Collections.Generic;
using System.Diagnostics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using JetBrains.Annotations;
using Microsoft.Extensions.Primitives;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace OpenIddict.Abstractions
{
@@ -35,7 +35,7 @@ namespace OpenIddict.Abstractions
/// Initializes a new OpenIddict response.
///
/// The response parameters.
- public OpenIddictResponse([NotNull] IEnumerable> parameters)
+ public OpenIddictResponse([NotNull] IEnumerable> parameters)
: base(parameters) { }
///
diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs
index 2859f277..1289e4e1 100644
--- a/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs
+++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs
@@ -11,7 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Newtonsoft.Json.Linq;
+using System.Text.Json;
namespace OpenIddict.Abstractions
{
@@ -212,7 +212,7 @@ namespace OpenIddict.Abstractions
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
///
- ValueTask GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken);
+ ValueTask> GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken);
///
/// Retrieves the callback addresses associated with an application.
@@ -341,7 +341,8 @@ namespace OpenIddict.Abstractions
/// The additional properties associated with the application.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken);
+ ValueTask SetPropertiesAsync([NotNull] TApplication application,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken);
///
/// Sets the callback addresses associated with an application.
diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs
index eecb897c..93ddf219 100644
--- a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs
@@ -11,7 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Newtonsoft.Json.Linq;
+using System.Text.Json;
namespace OpenIddict.Abstractions
{
@@ -183,7 +183,7 @@ namespace OpenIddict.Abstractions
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
///
- ValueTask GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
+ ValueTask> GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
///
/// Retrieves the scopes associated with an authorization.
@@ -285,7 +285,8 @@ namespace OpenIddict.Abstractions
/// The additional properties associated with the authorization.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken);
+ ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken);
///
/// Sets the scopes associated with an authorization.
diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs
index aee87eff..6876dd77 100644
--- a/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs
+++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs
@@ -11,7 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Newtonsoft.Json.Linq;
+using System.Text.Json;
namespace OpenIddict.Abstractions
{
@@ -166,7 +166,7 @@ namespace OpenIddict.Abstractions
/// A that can be used to monitor the asynchronous operation, whose
/// result returns all the additional properties associated with the scope.
///
- ValueTask GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken);
+ ValueTask> GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken);
///
/// Retrieves the resources associated with a scope.
@@ -245,7 +245,8 @@ namespace OpenIddict.Abstractions
/// The additional properties associated with the scope.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken);
+ ValueTask SetPropertiesAsync([NotNull] TScope scope,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken);
///
/// Sets the resources associated with a scope.
diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs
index 6b79c69f..5c0c2f93 100644
--- a/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs
+++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs
@@ -11,7 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Newtonsoft.Json.Linq;
+using System.Text.Json;
namespace OpenIddict.Abstractions
{
@@ -232,7 +232,7 @@ namespace OpenIddict.Abstractions
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
///
- ValueTask GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken);
+ ValueTask> GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken);
///
/// Retrieves the reference identifier associated with a token.
@@ -371,7 +371,8 @@ namespace OpenIddict.Abstractions
/// The additional properties associated with the token.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken);
+ ValueTask SetPropertiesAsync([NotNull] TToken token,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken);
///
/// Sets the reference identifier associated with a token.
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
index dc9c2a39..dfe26f3e 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
@@ -13,13 +13,13 @@ using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFramework.Models;
@@ -487,9 +487,7 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.Permissions)
- .Select(permission => (string) permission)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.Permissions);
});
return new ValueTask>(permissions);
@@ -524,9 +522,7 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.PostLogoutRedirectUris)
- .Select(address => (string) address)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.PostLogoutRedirectUris);
});
return new ValueTask>(addresses);
@@ -541,7 +537,7 @@ namespace OpenIddict.EntityFramework
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
@@ -550,7 +546,7 @@ namespace OpenIddict.EntityFramework
if (string.IsNullOrEmpty(application.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -561,10 +557,10 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(application.Properties);
+ return JsonSerializer.Deserialize>(application.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -596,9 +592,7 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.RedirectUris)
- .Select(address => (string) address)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.RedirectUris);
});
return new ValueTask>(addresses);
@@ -633,9 +627,7 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.Requirements)
- .Select(element => (string) element)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.Requirements);
});
return new ValueTask>(requirements);
@@ -836,7 +828,11 @@ namespace OpenIddict.EntityFramework
return default;
}
- application.Permissions = new JArray(permissions.ToArray()).ToString(Formatting.None);
+ application.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -863,7 +859,11 @@ namespace OpenIddict.EntityFramework
return default;
}
- application.PostLogoutRedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None);
+ application.PostLogoutRedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -875,21 +875,26 @@ namespace OpenIddict.EntityFramework
/// The additional properties associated with the application.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
application.Properties = null;
return default;
}
- application.Properties = properties.ToString(Formatting.None);
+ application.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -916,7 +921,11 @@ namespace OpenIddict.EntityFramework
return default;
}
- application.RedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None);
+ application.RedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -942,7 +951,11 @@ namespace OpenIddict.EntityFramework
return default;
}
- application.Requirements = new JArray(requirements.ToArray()).ToString(Formatting.None);
+ application.Requirements = JsonSerializer.Serialize(requirements, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
index ea3019b5..970642dd 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
@@ -13,13 +13,13 @@ using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFramework.Models;
@@ -488,7 +488,7 @@ namespace OpenIddict.EntityFramework
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
@@ -497,7 +497,7 @@ namespace OpenIddict.EntityFramework
if (string.IsNullOrEmpty(authorization.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -508,10 +508,10 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(authorization.Properties);
+ return JsonSerializer.Deserialize>(authorization.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -543,9 +543,7 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(authorization.Scopes)
- .Select(scope => (string) scope)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(authorization.Scopes);
});
return new ValueTask>(scopes);
@@ -820,21 +818,26 @@ namespace OpenIddict.EntityFramework
///
/// A that can be used to monitor the asynchronous operation.
///
- public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
authorization.Properties = null;
return default;
}
- authorization.Properties = properties.ToString(Formatting.None);
+ authorization.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -863,7 +866,11 @@ namespace OpenIddict.EntityFramework
return default;
}
- authorization.Scopes = new JArray(scopes.ToArray()).ToString(Formatting.None);
+ authorization.Scopes = JsonSerializer.Serialize(scopes, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs
index 00b01b95..ae0cc855 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs
@@ -12,13 +12,13 @@ using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFramework.Models;
@@ -357,7 +357,7 @@ namespace OpenIddict.EntityFramework
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the scope.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
@@ -366,7 +366,7 @@ namespace OpenIddict.EntityFramework
if (string.IsNullOrEmpty(scope.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -377,10 +377,10 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(scope.Properties);
+ return JsonSerializer.Deserialize>(scope.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -412,9 +412,7 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(scope.Resources)
- .Select(resource => (string) resource)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(scope.Resources);
});
return new ValueTask>(resources);
@@ -556,21 +554,26 @@ namespace OpenIddict.EntityFramework
/// The additional properties associated with the scope.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
scope.Properties = null;
return default;
}
- scope.Properties = properties.ToString(Formatting.None);
+ scope.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -596,7 +599,11 @@ namespace OpenIddict.EntityFramework
return default;
}
- scope.Resources = new JArray(resources.ToArray()).ToString(Formatting.None);
+ scope.Resources = JsonSerializer.Serialize(resources, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
index 914fb14f..907d488b 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
@@ -13,13 +13,13 @@ using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFramework.Models;
@@ -575,7 +575,7 @@ namespace OpenIddict.EntityFramework
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -584,7 +584,7 @@ namespace OpenIddict.EntityFramework
if (string.IsNullOrEmpty(token.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -595,10 +595,10 @@ namespace OpenIddict.EntityFramework
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(token.Properties);
+ return JsonSerializer.Deserialize>(token.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -987,21 +987,26 @@ namespace OpenIddict.EntityFramework
/// The additional properties associated with the token.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TToken token,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
token.Properties = null;
return default;
}
- token.Properties = properties.ToString(Formatting.None);
+ token.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
index fc2cdbdc..45ee2718 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
@@ -11,6 +11,8 @@ using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -19,8 +21,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFrameworkCore.Models;
@@ -534,9 +534,7 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.Permissions)
- .Select(permission => (string) permission)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.Permissions);
});
return new ValueTask>(permissions);
@@ -571,9 +569,7 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.PostLogoutRedirectUris)
- .Select(address => (string) address)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.PostLogoutRedirectUris);
});
return new ValueTask>(addresses);
@@ -588,7 +584,7 @@ namespace OpenIddict.EntityFrameworkCore
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
@@ -597,7 +593,7 @@ namespace OpenIddict.EntityFrameworkCore
if (string.IsNullOrEmpty(application.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -608,10 +604,10 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(application.Properties);
+ return JsonSerializer.Deserialize>(application.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -643,9 +639,7 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.RedirectUris)
- .Select(address => (string) address)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.RedirectUris);
});
return new ValueTask>(addresses);
@@ -680,9 +674,7 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.Requirements)
- .Select(element => (string) element)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.Requirements);
});
return new ValueTask>(requirements);
@@ -883,7 +875,11 @@ namespace OpenIddict.EntityFrameworkCore
return default;
}
- application.Permissions = new JArray(permissions.ToArray()).ToString(Formatting.None);
+ application.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -910,7 +906,11 @@ namespace OpenIddict.EntityFrameworkCore
return default;
}
- application.PostLogoutRedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None);
+ application.PostLogoutRedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -922,21 +922,26 @@ namespace OpenIddict.EntityFrameworkCore
/// The additional properties associated with the application.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
application.Properties = null;
return default;
}
- application.Properties = properties.ToString(Formatting.None);
+ application.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -963,7 +968,11 @@ namespace OpenIddict.EntityFrameworkCore
return default;
}
- application.RedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None);
+ application.RedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -989,7 +998,11 @@ namespace OpenIddict.EntityFrameworkCore
return default;
}
- application.Requirements = new JArray(requirements.ToArray()).ToString(Formatting.None);
+ application.Requirements = JsonSerializer.Serialize(requirements, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
index 118e65ec..0887f2e4 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
@@ -11,6 +11,8 @@ using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -19,8 +21,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFrameworkCore.Models;
@@ -542,7 +542,7 @@ namespace OpenIddict.EntityFrameworkCore
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
@@ -551,7 +551,7 @@ namespace OpenIddict.EntityFrameworkCore
if (string.IsNullOrEmpty(authorization.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -562,10 +562,10 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(authorization.Properties);
+ return JsonSerializer.Deserialize>(authorization.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -597,9 +597,7 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(authorization.Scopes)
- .Select(scope => (string) scope)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(authorization.Scopes);
});
return new ValueTask>(scopes);
@@ -888,21 +886,26 @@ namespace OpenIddict.EntityFrameworkCore
/// The additional properties associated with the authorization.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
authorization.Properties = null;
return default;
}
- authorization.Properties = properties.ToString(Formatting.None);
+ authorization.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -929,7 +932,11 @@ namespace OpenIddict.EntityFrameworkCore
return default;
}
- authorization.Scopes = new JArray(scopes.ToArray()).ToString(Formatting.None);
+ authorization.Scopes = JsonSerializer.Serialize(scopes, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
index 54205bf6..00bf2e56 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
@@ -10,14 +10,14 @@ using System.Collections.Immutable;
using System.ComponentModel;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFrameworkCore.Models;
@@ -372,7 +372,7 @@ namespace OpenIddict.EntityFrameworkCore
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the scope.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
@@ -381,7 +381,7 @@ namespace OpenIddict.EntityFrameworkCore
if (string.IsNullOrEmpty(scope.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -392,10 +392,10 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(scope.Properties);
+ return JsonSerializer.Deserialize>(scope.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -427,9 +427,7 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(scope.Resources)
- .Select(resource => (string) resource)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(scope.Resources);
});
return new ValueTask>(resources);
@@ -571,21 +569,26 @@ namespace OpenIddict.EntityFrameworkCore
/// The additional properties associated with the scope.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
scope.Properties = null;
return default;
}
- scope.Properties = properties.ToString(Formatting.None);
+ scope.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -611,7 +614,11 @@ namespace OpenIddict.EntityFrameworkCore
return default;
}
- scope.Resources = new JArray(resources.ToArray()).ToString(Formatting.None);
+ scope.Resources = JsonSerializer.Serialize(resources, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
index 6c357d28..6475338c 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
@@ -6,10 +6,13 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -18,8 +21,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.EntityFrameworkCore.Models;
@@ -621,7 +622,7 @@ namespace OpenIddict.EntityFrameworkCore
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -630,7 +631,7 @@ namespace OpenIddict.EntityFrameworkCore
if (string.IsNullOrEmpty(token.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
// Note: parsing the stringified properties is an expensive operation.
@@ -641,10 +642,10 @@ namespace OpenIddict.EntityFrameworkCore
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JObject.Parse(token.Properties);
+ return JsonSerializer.Deserialize>(token.Properties);
});
- return new ValueTask((JObject) properties.DeepClone());
+ return new ValueTask>(properties);
}
///
@@ -1068,21 +1069,26 @@ namespace OpenIddict.EntityFrameworkCore
///
/// A that can be used to monitor the asynchronous operation.
///
- public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TToken token,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
token.Properties = null;
return default;
}
- token.Properties = properties.ToString(Formatting.None);
+ token.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs
index 000a2648..0a7c8ddf 100644
--- a/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.MongoDb/Stores/OpenIddictApplicationStore.cs
@@ -17,8 +17,6 @@ using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.MongoDb.Models;
@@ -448,7 +446,7 @@ namespace OpenIddict.MongoDb
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
@@ -457,10 +455,10 @@ namespace OpenIddict.MongoDb
if (application.Properties == null)
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.FromObject(application.Properties.ToDictionary()));
+ return new ValueTask>(application.Properties.ToDictionary().ToImmutableDictionary());
}
///
@@ -763,21 +761,22 @@ namespace OpenIddict.MongoDb
/// The additional properties associated with the application.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
application.Properties = null;
return default;
}
- application.Properties = BsonDocument.Parse(properties.ToString(Formatting.None));
+ application.Properties = new BsonDocument(properties.ToDictionary(property => property.Key, property => property.Value));
return default;
}
diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs
index 381a3e52..16e7a51f 100644
--- a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs
@@ -17,8 +17,6 @@ using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.MongoDb.Models;
@@ -482,7 +480,7 @@ namespace OpenIddict.MongoDb
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
@@ -491,10 +489,10 @@ namespace OpenIddict.MongoDb
if (authorization.Properties == null)
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.FromObject(authorization.Properties.ToDictionary()));
+ return new ValueTask>(authorization.Properties.ToDictionary().ToImmutableDictionary());
}
///
@@ -766,21 +764,22 @@ namespace OpenIddict.MongoDb
/// The additional properties associated with the authorization.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
authorization.Properties = null;
return default;
}
- authorization.Properties = BsonDocument.Parse(properties.ToString(Formatting.None));
+ authorization.Properties = new BsonDocument(properties.ToDictionary(property => property.Key, property => property.Value));
return default;
}
diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs
index 3c67375e..ced982bd 100644
--- a/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs
+++ b/src/OpenIddict.MongoDb/Stores/OpenIddictScopeStore.cs
@@ -10,6 +10,8 @@ using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -17,8 +19,6 @@ using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.MongoDb.Models;
@@ -349,7 +349,7 @@ namespace OpenIddict.MongoDb
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the scope.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
@@ -358,10 +358,10 @@ namespace OpenIddict.MongoDb
if (scope.Properties == null)
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.FromObject(scope.Properties.ToDictionary()));
+ return new ValueTask>(scope.Properties.ToDictionary().ToImmutableDictionary());
}
///
@@ -541,21 +541,22 @@ namespace OpenIddict.MongoDb
/// The additional properties associated with the scope.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
scope.Properties = null;
return default;
}
- scope.Properties = BsonDocument.Parse(properties.ToString(Formatting.None));
+ scope.Properties = new BsonDocument(properties.ToDictionary(property => property.Key, property => property.Value));
return default;
}
diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs
index d0aaef0d..2394b474 100644
--- a/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.MongoDb/Stores/OpenIddictTokenStore.cs
@@ -17,8 +17,6 @@ using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using OpenIddict.MongoDb.Models;
@@ -547,7 +545,7 @@ namespace OpenIddict.MongoDb
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -556,10 +554,10 @@ namespace OpenIddict.MongoDb
if (token.Properties == null)
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.FromObject(token.Properties.ToDictionary()));
+ return new ValueTask>(token.Properties.ToDictionary().ToImmutableDictionary());
}
///
@@ -865,21 +863,22 @@ namespace OpenIddict.MongoDb
/// The additional properties associated with the token.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TToken token,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
token.Properties = null;
return default;
}
- token.Properties = BsonDocument.Parse(properties.ToString(Formatting.None));
+ token.Properties = new BsonDocument(properties.ToDictionary(property => property.Key, property => property.Value));
return default;
}
diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs
index e202d34a..eb808ce2 100644
--- a/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs
@@ -11,13 +11,13 @@ using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Linq;
@@ -474,9 +474,7 @@ namespace OpenIddict.NHibernate
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.Permissions)
- .Select(permission => (string) permission)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.Permissions);
});
return new ValueTask>(permissions);
@@ -511,9 +509,7 @@ namespace OpenIddict.NHibernate
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.PostLogoutRedirectUris)
- .Select(address => (string) address)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.PostLogoutRedirectUris);
});
return new ValueTask>(addresses);
@@ -528,7 +524,7 @@ namespace OpenIddict.NHibernate
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
@@ -537,10 +533,21 @@ namespace OpenIddict.NHibernate
if (string.IsNullOrEmpty(application.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.Parse(application.Properties));
+ // Note: parsing the stringified properties is an expensive operation.
+ // To mitigate that, the resulting object is stored in the memory cache.
+ var key = string.Concat("2e3e9680-5654-48d8-a27d-b8bb4f0f1d50", "\x1e", application.Properties);
+ var properties = Cache.GetOrCreate(key, entry =>
+ {
+ entry.SetPriority(CacheItemPriority.High)
+ .SetSlidingExpiration(TimeSpan.FromMinutes(1));
+
+ return JsonSerializer.Deserialize>(application.Properties);
+ });
+
+ return new ValueTask>(properties);
}
///
@@ -572,9 +579,7 @@ namespace OpenIddict.NHibernate
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.RedirectUris)
- .Select(address => (string) address)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.RedirectUris);
});
return new ValueTask>(addresses);
@@ -609,9 +614,7 @@ namespace OpenIddict.NHibernate
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(application.Requirements)
- .Select(element => (string) element)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(application.Requirements);
});
return new ValueTask>(requirements);
@@ -828,7 +831,11 @@ namespace OpenIddict.NHibernate
return default;
}
- application.Permissions = new JArray(permissions.ToArray()).ToString(Formatting.None);
+ application.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -855,7 +862,11 @@ namespace OpenIddict.NHibernate
return default;
}
- application.PostLogoutRedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None);
+ application.PostLogoutRedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -867,21 +878,26 @@ namespace OpenIddict.NHibernate
/// The additional properties associated with the application.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TApplication application,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
application.Properties = null;
return default;
}
- application.Properties = properties.ToString(Formatting.None);
+ application.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -908,7 +924,11 @@ namespace OpenIddict.NHibernate
return default;
}
- application.RedirectUris = new JArray(addresses.ToArray()).ToString(Formatting.None);
+ application.RedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -934,7 +954,11 @@ namespace OpenIddict.NHibernate
return default;
}
- application.Requirements = new JArray(requirements.ToArray()).ToString(Formatting.None);
+ application.Requirements = JsonSerializer.Serialize(requirements, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs
index 023ffcca..c25c0ac7 100644
--- a/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs
@@ -11,13 +11,13 @@ using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Linq;
@@ -506,7 +506,7 @@ namespace OpenIddict.NHibernate
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
@@ -515,10 +515,21 @@ namespace OpenIddict.NHibernate
if (string.IsNullOrEmpty(authorization.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.Parse(authorization.Properties));
+ // Note: parsing the stringified properties is an expensive operation.
+ // To mitigate that, the resulting object is stored in the memory cache.
+ var key = string.Concat("68056e1a-dbcf-412b-9a6a-d791c7dbe726", "\x1e", authorization.Properties);
+ var properties = Cache.GetOrCreate(key, entry =>
+ {
+ entry.SetPriority(CacheItemPriority.High)
+ .SetSlidingExpiration(TimeSpan.FromMinutes(1));
+
+ return JsonSerializer.Deserialize>(authorization.Properties);
+ });
+
+ return new ValueTask>(properties);
}
///
@@ -550,9 +561,7 @@ namespace OpenIddict.NHibernate
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(authorization.Scopes)
- .Select(scope => (string) scope)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(authorization.Scopes);
});
return new ValueTask>(scopes);
@@ -763,21 +772,26 @@ namespace OpenIddict.NHibernate
/// The additional properties associated with the authorization.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
authorization.Properties = null;
return default;
}
- authorization.Properties = properties.ToString(Formatting.None);
+ authorization.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -804,7 +818,11 @@ namespace OpenIddict.NHibernate
return default;
}
- authorization.Scopes = new JArray(scopes.ToArray()).ToString(Formatting.None);
+ authorization.Scopes = JsonSerializer.Serialize(scopes, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs
index 1c0b3b39..f325a07f 100644
--- a/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs
+++ b/src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs
@@ -11,13 +11,13 @@ using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Linq;
@@ -390,7 +390,7 @@ namespace OpenIddict.NHibernate
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the scope.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
@@ -399,10 +399,21 @@ namespace OpenIddict.NHibernate
if (string.IsNullOrEmpty(scope.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.Parse(scope.Properties));
+ // Note: parsing the stringified properties is an expensive operation.
+ // To mitigate that, the resulting object is stored in the memory cache.
+ var key = string.Concat("78d8dfdd-3870-442e-b62e-dc9bf6eaeff7", "\x1e", scope.Properties);
+ var properties = Cache.GetOrCreate(key, entry =>
+ {
+ entry.SetPriority(CacheItemPriority.High)
+ .SetSlidingExpiration(TimeSpan.FromMinutes(1));
+
+ return JsonSerializer.Deserialize>(scope.Properties);
+ });
+
+ return new ValueTask>(properties);
}
///
@@ -434,9 +445,7 @@ namespace OpenIddict.NHibernate
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
- return JArray.Parse(scope.Resources)
- .Select(resource => (string) resource)
- .ToImmutableArray();
+ return JsonSerializer.Deserialize>(scope.Resources);
});
return new ValueTask>(resources);
@@ -594,21 +603,26 @@ namespace OpenIddict.NHibernate
/// The additional properties associated with the scope.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
scope.Properties = null;
return default;
}
- scope.Properties = properties.ToString(Formatting.None);
+ scope.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
@@ -634,7 +648,11 @@ namespace OpenIddict.NHibernate
return default;
}
- scope.Resources = new JArray(resources.ToArray()).ToString(Formatting.None);
+ scope.Resources = JsonSerializer.Serialize(resources, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs
index 610e822d..a5c859c0 100644
--- a/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs
@@ -11,13 +11,13 @@ using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Linq;
@@ -626,7 +626,7 @@ namespace OpenIddict.NHibernate
/// A that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
///
- public virtual ValueTask GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ public virtual ValueTask> GetPropertiesAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -635,10 +635,21 @@ namespace OpenIddict.NHibernate
if (string.IsNullOrEmpty(token.Properties))
{
- return new ValueTask(new JObject());
+ return new ValueTask>(ImmutableDictionary.Create());
}
- return new ValueTask(JObject.Parse(token.Properties));
+ // Note: parsing the stringified properties is an expensive operation.
+ // To mitigate that, the resulting object is stored in the memory cache.
+ var key = string.Concat("d0509397-1bbf-40e7-97e1-5e6d7bc2536c", "\x1e", token.Properties);
+ var properties = Cache.GetOrCreate(key, entry =>
+ {
+ entry.SetPriority(CacheItemPriority.High)
+ .SetSlidingExpiration(TimeSpan.FromMinutes(1));
+
+ return JsonSerializer.Deserialize>(token.Properties);
+ });
+
+ return new ValueTask>(properties);
}
///
@@ -951,21 +962,26 @@ namespace OpenIddict.NHibernate
/// The additional properties associated with the token.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual ValueTask SetPropertiesAsync([NotNull] TToken token, [CanBeNull] JObject properties, CancellationToken cancellationToken)
+ public virtual ValueTask SetPropertiesAsync([NotNull] TToken token,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
- if (properties == null)
+ if (properties == null || properties.IsEmpty)
{
token.Properties = null;
return default;
}
- token.Properties = properties.ToString(Formatting.None);
+ token.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
return default;
}
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddict.Server.AspNetCore.csproj b/src/OpenIddict.Server.AspNetCore/OpenIddict.Server.AspNetCore.csproj
index 8cb53981..bc26e35a 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddict.Server.AspNetCore.csproj
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddict.Server.AspNetCore.csproj
@@ -25,7 +25,6 @@
-
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConstants.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConstants.cs
index a9b55b23..846ecbc1 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConstants.cs
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConstants.cs
@@ -17,6 +17,12 @@ namespace OpenIddict.Server.AspNetCore
public const string LogoutRequest = "openiddict-logout-request:";
}
+ public static class JsonWebTokenTypes
+ {
+ public const string AuthorizationRequest = "oi_auth_req";
+ public const string LogoutRequest = "oi_lgt_req";
+ }
+
public static class Properties
{
public const string Error = ".error";
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs
index 4e71c24b..142355e3 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs
@@ -5,12 +5,15 @@
*/
using System;
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
+using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore;
@@ -18,14 +21,13 @@ using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Bson;
-using Newtonsoft.Json.Linq;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreConstants;
using static OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlerFilters;
using static OpenIddict.Server.OpenIddictServerEvents;
+using JsonWebTokenTypes = OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreConstants.JsonWebTokenTypes;
namespace OpenIddict.Server.AspNetCore
{
@@ -110,8 +112,8 @@ namespace OpenIddict.Server.AspNetCore
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- var payload = await _cache.GetAsync(Cache.AuthorizationRequest + context.Request.RequestId);
- if (payload == null)
+ var token = await _cache.GetStringAsync(Cache.AuthorizationRequest + context.Request.RequestId);
+ if (token == null || !context.Options.JsonWebTokenHandler.CanReadToken(token))
{
context.Logger.LogError("The authorization request was rejected because an unknown " +
"or invalid request_id parameter was specified.");
@@ -124,16 +126,45 @@ namespace OpenIddict.Server.AspNetCore
}
// Restore the authorization request parameters from the serialized payload.
- using var reader = new BsonDataReader(new MemoryStream(payload));
- foreach (var parameter in JObject.Load(reader))
+ var parameters = new TokenValidationParameters
+ {
+ IssuerSigningKeys = context.Options.SigningCredentials.Select(credentials => credentials.Key),
+ TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key),
+ ValidateLifetime = false,
+ ValidAudience = context.Issuer.AbsoluteUri,
+ ValidIssuer = context.Issuer.AbsoluteUri,
+ ValidTypes = new[] { JsonWebTokenTypes.AuthorizationRequest }
+ };
+
+ var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(token, parameters);
+ if (!result.IsValid)
+ {
+ context.Logger.LogError("The authorization request was rejected because an unknown " +
+ "or invalid request_id parameter was specified.");
+
+ context.Reject(
+ error: Errors.InvalidRequest,
+ description: "The specified 'request_id' parameter is invalid.");
+
+ return;
+ }
+
+ using var document = JsonDocument.Parse(
+ Base64UrlEncoder.Decode(((JsonWebToken) result.SecurityToken).InnerToken.EncodedPayload));
+ if (document.RootElement.ValueKind != JsonValueKind.Object)
+ {
+ throw new InvalidOperationException("The authorization request payload is malformed.");
+ }
+
+ foreach (var parameter in document.RootElement.EnumerateObject())
{
// Avoid overriding the current request parameters.
- if (context.Request.HasParameter(parameter.Key))
+ if (context.Request.HasParameter(parameter.Name))
{
continue;
}
- context.Request.SetParameter(parameter.Key, parameter.Value);
+ context.Request.SetParameter(parameter.Name, parameter.Value.Clone());
}
}
}
@@ -216,19 +247,32 @@ namespace OpenIddict.Server.AspNetCore
context.Request.RequestId = Base64UrlEncoder.Encode(data);
// Store the serialized authorization request parameters in the distributed cache.
- var stream = new MemoryStream();
- using (var writer = new BsonDataWriter(stream))
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(new SecurityTokenDescriptor
{
- writer.CloseOutput = false;
-
- var serializer = JsonSerializer.CreateDefault();
- serializer.Serialize(writer, context.Request);
- }
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.AuthorizationRequest
+ },
+ Audience = context.Issuer.AbsoluteUri,
+ Claims = context.Request.GetParameters().ToDictionary(
+ parameter => parameter.Key,
+ parameter => parameter.Value.Value),
+ Issuer = context.Issuer.AbsoluteUri,
+ SigningCredentials = context.Options.SigningCredentials.First(),
+ Subject = new ClaimsIdentity()
+ });
+
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ encryptingCredentials: context.Options.EncryptionCredentials.First(),
+ additionalHeaderClaims: new Dictionary
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.AuthorizationRequest
+ });
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- await _cache.SetAsync(Cache.AuthorizationRequest + context.Request.RequestId,
- stream.ToArray(), _options.CurrentValue.AuthorizationEndpointCachingPolicy);
+ await _cache.SetStringAsync(Cache.AuthorizationRequest + context.Request.RequestId,
+ token, _options.CurrentValue.AuthorizationEndpointCachingPolicy);
// Create a new GET authorization request containing only the request_id parameter.
var address = QueryHelpers.AddQueryString(
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs
index a139eaa7..29d2f084 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs
@@ -5,11 +5,13 @@
*/
using System;
+using System.Collections.Generic;
using System.Collections.Immutable;
-using System.IO;
using System.Linq;
+using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore;
@@ -17,14 +19,13 @@ using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Bson;
-using Newtonsoft.Json.Linq;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreConstants;
using static OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlerFilters;
using static OpenIddict.Server.OpenIddictServerEvents;
+using JsonWebTokenTypes = OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreConstants.JsonWebTokenTypes;
namespace OpenIddict.Server.AspNetCore
{
@@ -109,8 +110,8 @@ namespace OpenIddict.Server.AspNetCore
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- var payload = await _cache.GetAsync(Cache.LogoutRequest + context.Request.RequestId);
- if (payload == null)
+ var token = await _cache.GetStringAsync(Cache.LogoutRequest + context.Request.RequestId);
+ if (token == null || !context.Options.JsonWebTokenHandler.CanReadToken(token))
{
context.Logger.LogError("The logout request was rejected because an unknown " +
"or invalid request_id parameter was specified.");
@@ -122,17 +123,46 @@ namespace OpenIddict.Server.AspNetCore
return;
}
- // Restore the logout request parameters from the serialized payload.
- using var reader = new BsonDataReader(new MemoryStream(payload));
- foreach (var parameter in JObject.Load(reader))
+ // Restore the authorization request parameters from the serialized payload.
+ var parameters = new TokenValidationParameters
+ {
+ IssuerSigningKeys = context.Options.SigningCredentials.Select(credentials => credentials.Key),
+ TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key),
+ ValidateLifetime = false,
+ ValidAudience = context.Issuer.AbsoluteUri,
+ ValidIssuer = context.Issuer.AbsoluteUri,
+ ValidTypes = new[] { JsonWebTokenTypes.LogoutRequest }
+ };
+
+ var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(token, parameters);
+ if (!result.IsValid)
+ {
+ context.Logger.LogError("The logout request was rejected because an unknown " +
+ "or invalid request_id parameter was specified.");
+
+ context.Reject(
+ error: Errors.InvalidRequest,
+ description: "The specified 'request_id' parameter is invalid.");
+
+ return;
+ }
+
+ using var document = JsonDocument.Parse(
+ Base64UrlEncoder.Decode(((JsonWebToken) result.SecurityToken).InnerToken.EncodedPayload));
+ if (document.RootElement.ValueKind != JsonValueKind.Object)
+ {
+ throw new InvalidOperationException("The logout request payload is malformed.");
+ }
+
+ foreach (var parameter in document.RootElement.EnumerateObject())
{
// Avoid overriding the current request parameters.
- if (context.Request.HasParameter(parameter.Key))
+ if (context.Request.HasParameter(parameter.Name))
{
continue;
}
- context.Request.SetParameter(parameter.Key, parameter.Value);
+ context.Request.SetParameter(parameter.Name, parameter.Value.Clone());
}
}
}
@@ -215,19 +245,32 @@ namespace OpenIddict.Server.AspNetCore
context.Request.RequestId = Base64UrlEncoder.Encode(data);
// Store the serialized logout request parameters in the distributed cache.
- var stream = new MemoryStream();
- using (var writer = new BsonDataWriter(stream))
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(new SecurityTokenDescriptor
{
- writer.CloseOutput = false;
-
- var serializer = JsonSerializer.CreateDefault();
- serializer.Serialize(writer, context.Request);
- }
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.LogoutRequest
+ },
+ Audience = context.Issuer.AbsoluteUri,
+ Claims = context.Request.GetParameters().ToDictionary(
+ parameter => parameter.Key,
+ parameter => parameter.Value.Value),
+ Issuer = context.Issuer.AbsoluteUri,
+ SigningCredentials = context.Options.SigningCredentials.First(),
+ Subject = new ClaimsIdentity()
+ });
+
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ encryptingCredentials: context.Options.EncryptionCredentials.First(),
+ additionalHeaderClaims: new Dictionary
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.AuthorizationRequest
+ });
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- await _cache.SetAsync(Cache.LogoutRequest + context.Request.RequestId,
- stream.ToArray(), _options.CurrentValue.LogoutEndpointCachingPolicy);
+ await _cache.SetStringAsync(Cache.LogoutRequest + context.Request.RequestId,
+ token, _options.CurrentValue.AuthorizationEndpointCachingPolicy);
// Create a new GET logout request containing only the request_id parameter.
var address = QueryHelpers.AddQueryString(
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs
index 2cb3e9a1..df7663aa 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs
@@ -10,6 +10,8 @@ using System.Collections.Immutable;
using System.ComponentModel;
using System.IO;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore;
@@ -18,7 +20,6 @@ using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
-using Newtonsoft.Json;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlerFilters;
@@ -834,61 +835,59 @@ namespace OpenIddict.Server.AspNetCore
context.Logger.LogInformation("The response was successfully returned as a JSON document: {Response}.", context.Response);
- using (var buffer = new MemoryStream())
- using (var writer = new JsonTextWriter(new StreamWriter(buffer)))
+ using var stream = new MemoryStream();
+ await JsonSerializer.SerializeAsync(stream, context.Response, new JsonSerializerOptions
{
- var serializer = JsonSerializer.CreateDefault();
- serializer.Serialize(writer, context.Response);
-
- writer.Flush();
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
- if (!string.IsNullOrEmpty(context.Response.Error))
+ if (!string.IsNullOrEmpty(context.Response.Error))
+ {
+ // When client authentication is made using basic authentication, the authorization server MUST return
+ // a 401 response with a valid WWW-Authenticate header containing the Basic scheme and a non-empty realm.
+ // A similar error MAY be returned even when basic authentication is not used and MUST also be returned
+ // when an invalid token is received by the userinfo endpoint using the Bearer authentication scheme.
+ // To simplify the logic, a 401 response with the Bearer scheme is returned for invalid_token errors
+ // and a 401 response with the Basic scheme is returned for invalid_client, even if the credentials
+ // were specified in the request form instead of the HTTP headers, as allowed by the specification.
+ var scheme = context.Response.Error switch
{
- // When client authentication is made using basic authentication, the authorization server MUST return
- // a 401 response with a valid WWW-Authenticate header containing the Basic scheme and a non-empty realm.
- // A similar error MAY be returned even when basic authentication is not used and MUST also be returned
- // when an invalid token is received by the userinfo endpoint using the Bearer authentication scheme.
- // To simplify the logic, a 401 response with the Bearer scheme is returned for invalid_token errors
- // and a 401 response with the Basic scheme is returned for invalid_client, even if the credentials
- // were specified in the request form instead of the HTTP headers, as allowed by the specification.
- var scheme = context.Response.Error switch
- {
- Errors.InvalidClient => Schemes.Basic,
- Errors.InvalidToken => Schemes.Bearer,
- _ => null
- };
+ Errors.InvalidClient => Schemes.Basic,
+ Errors.InvalidToken => Schemes.Bearer,
+ _ => null
+ };
- if (!string.IsNullOrEmpty(scheme))
+ if (!string.IsNullOrEmpty(scheme))
+ {
+ if (context.Issuer == null)
{
- if (context.Issuer == null)
- {
- throw new InvalidOperationException("The issuer address cannot be inferred from the current request.");
- }
+ throw new InvalidOperationException("The issuer address cannot be inferred from the current request.");
+ }
- request.HttpContext.Response.StatusCode = 401;
+ request.HttpContext.Response.StatusCode = 401;
- request.HttpContext.Response.Headers[HeaderNames.WWWAuthenticate] = new StringBuilder()
- .Append(scheme)
- .Append(' ')
- .Append(Parameters.Realm)
- .Append("=\"")
- .Append(context.Issuer.AbsoluteUri)
- .Append('"')
- .ToString();
- }
+ request.HttpContext.Response.Headers[HeaderNames.WWWAuthenticate] = new StringBuilder()
+ .Append(scheme)
+ .Append(' ')
+ .Append(Parameters.Realm)
+ .Append("=\"")
+ .Append(context.Issuer.AbsoluteUri)
+ .Append('"')
+ .ToString();
+ }
- else
- {
- request.HttpContext.Response.StatusCode = 400;
- }
+ else
+ {
+ request.HttpContext.Response.StatusCode = 400;
}
+ }
- request.HttpContext.Response.ContentLength = buffer.Length;
- request.HttpContext.Response.ContentType = "application/json;charset=UTF-8";
+ request.HttpContext.Response.ContentLength = stream.Length;
+ request.HttpContext.Response.ContentType = "application/json;charset=UTF-8";
- buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
- await buffer.CopyToAsync(request.HttpContext.Response.Body, 4096, request.HttpContext.RequestAborted);
- }
+ stream.Seek(offset: 0, loc: SeekOrigin.Begin);
+ await stream.CopyToAsync(request.HttpContext.Response.Body, 4096, request.HttpContext.RequestAborted);
context.HandleRequest();
}
diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs
index 837f35e0..984e4589 100644
--- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs
+++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs
@@ -11,9 +11,9 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using JetBrains.Annotations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using Properties = OpenIddict.Server.DataProtection.OpenIddictServerDataProtectionConstants.Properties;
@@ -175,7 +175,7 @@ namespace OpenIddict.Server.DataProtection
static ImmutableArray GetArrayProperty(IReadOnlyDictionary properties, string name)
=> properties.TryGetValue(name, out var value) ?
- JArray.Parse(value).Values().ToImmutableArray() : ImmutableArray.Create();
+ JsonSerializer.Deserialize>(value) : ImmutableArray.Create();
static DateTimeOffset? GetDateProperty(IReadOnlyDictionary properties, string name)
=> properties.TryGetValue(name, out var value) ? (DateTimeOffset?)
@@ -362,17 +362,20 @@ namespace OpenIddict.Server.DataProtection
}
}
- static void SetArrayProperty(IDictionary properties, string name, IEnumerable values)
+ static void SetArrayProperty(IDictionary properties, string name, ImmutableArray values)
{
- var array = new JArray(values);
- if (array.Count == 0)
+ if (values.IsDefaultOrEmpty)
{
properties.Remove(name);
}
else
{
- properties[name] = array.ToString(Formatting.None);
+ properties[name] = JsonSerializer.Serialize(values, new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
}
}
}
diff --git a/src/OpenIddict.Server.Owin/OpenIddict.Server.Owin.csproj b/src/OpenIddict.Server.Owin/OpenIddict.Server.Owin.csproj
index cc31469d..afc864d2 100644
--- a/src/OpenIddict.Server.Owin/OpenIddict.Server.Owin.csproj
+++ b/src/OpenIddict.Server.Owin/OpenIddict.Server.Owin.csproj
@@ -18,7 +18,6 @@
-
diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinConstants.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinConstants.cs
index e1b18c0a..1d4e6639 100644
--- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinConstants.cs
+++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinConstants.cs
@@ -17,6 +17,12 @@ namespace OpenIddict.Server.Owin
public const string LogoutRequest = "openiddict-logout-request:";
}
+ public static class JsonWebTokenTypes
+ {
+ public const string AuthorizationRequest = "oi_auth_req";
+ public const string LogoutRequest = "oi_lgt_req";
+ }
+
public static class Properties
{
public const string Error = ".error";
diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs
index 17eaa4de..58fe2381 100644
--- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs
+++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs
@@ -5,27 +5,29 @@
*/
using System;
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
+using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Infrastructure;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Bson;
-using Newtonsoft.Json.Linq;
using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.Owin.OpenIddictServerOwinConstants;
using static OpenIddict.Server.Owin.OpenIddictServerOwinHandlerFilters;
+using JsonWebTokenTypes = OpenIddict.Server.Owin.OpenIddictServerOwinConstants.JsonWebTokenTypes;
namespace OpenIddict.Server.Owin
{
@@ -109,8 +111,8 @@ namespace OpenIddict.Server.Owin
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- var payload = await _cache.GetAsync(Cache.AuthorizationRequest + context.Request.RequestId);
- if (payload == null)
+ var token = await _cache.GetStringAsync(Cache.AuthorizationRequest + context.Request.RequestId);
+ if (token == null || !context.Options.JsonWebTokenHandler.CanReadToken(token))
{
context.Logger.LogError("The authorization request was rejected because an unknown " +
"or invalid request_id parameter was specified.");
@@ -123,16 +125,45 @@ namespace OpenIddict.Server.Owin
}
// Restore the authorization request parameters from the serialized payload.
- using var reader = new BsonDataReader(new MemoryStream(payload));
- foreach (var parameter in JObject.Load(reader))
+ var parameters = new TokenValidationParameters
+ {
+ IssuerSigningKeys = context.Options.SigningCredentials.Select(credentials => credentials.Key),
+ TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key),
+ ValidateLifetime = false,
+ ValidAudience = context.Issuer.AbsoluteUri,
+ ValidIssuer = context.Issuer.AbsoluteUri,
+ ValidTypes = new[] { JsonWebTokenTypes.AuthorizationRequest }
+ };
+
+ var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(token, parameters);
+ if (!result.IsValid)
+ {
+ context.Logger.LogError("The authorization request was rejected because an unknown " +
+ "or invalid request_id parameter was specified.");
+
+ context.Reject(
+ error: Errors.InvalidRequest,
+ description: "The specified 'request_id' parameter is invalid.");
+
+ return;
+ }
+
+ using var document = JsonDocument.Parse(
+ Base64UrlEncoder.Decode(((JsonWebToken) result.SecurityToken).InnerToken.EncodedPayload));
+ if (document.RootElement.ValueKind != JsonValueKind.Object)
+ {
+ throw new InvalidOperationException("The authorization request payload is malformed.");
+ }
+
+ foreach (var parameter in document.RootElement.EnumerateObject())
{
// Avoid overriding the current request parameters.
- if (context.Request.HasParameter(parameter.Key))
+ if (context.Request.HasParameter(parameter.Name))
{
continue;
}
- context.Request.SetParameter(parameter.Key, parameter.Value);
+ context.Request.SetParameter(parameter.Name, parameter.Value.Clone());
}
}
}
@@ -210,19 +241,32 @@ namespace OpenIddict.Server.Owin
context.Request.RequestId = Base64UrlEncoder.Encode(data);
// Store the serialized authorization request parameters in the distributed cache.
- var stream = new MemoryStream();
- using (var writer = new BsonDataWriter(stream))
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(new SecurityTokenDescriptor
{
- writer.CloseOutput = false;
-
- var serializer = JsonSerializer.CreateDefault();
- serializer.Serialize(writer, context.Request);
- }
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.AuthorizationRequest
+ },
+ Audience = context.Issuer.AbsoluteUri,
+ Claims = context.Request.GetParameters().ToDictionary(
+ parameter => parameter.Key,
+ parameter => parameter.Value.Value),
+ Issuer = context.Issuer.AbsoluteUri,
+ SigningCredentials = context.Options.SigningCredentials.First(),
+ Subject = new ClaimsIdentity()
+ });
+
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ encryptingCredentials: context.Options.EncryptionCredentials.First(),
+ additionalHeaderClaims: new Dictionary
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.AuthorizationRequest
+ });
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- await _cache.SetAsync(Cache.AuthorizationRequest + context.Request.RequestId,
- stream.ToArray(), _options.CurrentValue.AuthorizationEndpointCachingPolicy);
+ await _cache.SetStringAsync(Cache.AuthorizationRequest + context.Request.RequestId,
+ token, _options.CurrentValue.AuthorizationEndpointCachingPolicy);
// Create a new GET authorization request containing only the request_id parameter.
var address = WebUtilities.AddQueryString(
diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs
index f02f01c9..c6202106 100644
--- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs
+++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs
@@ -5,26 +5,27 @@
*/
using System;
+using System.Collections.Generic;
using System.Collections.Immutable;
-using System.IO;
using System.Linq;
+using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Infrastructure;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Bson;
-using Newtonsoft.Json.Linq;
using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.Owin.OpenIddictServerOwinConstants;
using static OpenIddict.Server.Owin.OpenIddictServerOwinHandlerFilters;
+using JsonWebTokenTypes = OpenIddict.Server.Owin.OpenIddictServerOwinConstants.JsonWebTokenTypes;
namespace OpenIddict.Server.Owin
{
@@ -108,8 +109,8 @@ namespace OpenIddict.Server.Owin
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- var payload = await _cache.GetAsync(Cache.LogoutRequest + context.Request.RequestId);
- if (payload == null)
+ var token = await _cache.GetStringAsync(Cache.LogoutRequest + context.Request.RequestId);
+ if (token == null || !context.Options.JsonWebTokenHandler.CanReadToken(token))
{
context.Logger.LogError("The logout request was rejected because an unknown " +
"or invalid request_id parameter was specified.");
@@ -121,17 +122,46 @@ namespace OpenIddict.Server.Owin
return;
}
- // Restore the logout request parameters from the serialized payload.
- using var reader = new BsonDataReader(new MemoryStream(payload));
- foreach (var parameter in JObject.Load(reader))
+ // Restore the authorization request parameters from the serialized payload.
+ var parameters = new TokenValidationParameters
+ {
+ IssuerSigningKeys = context.Options.SigningCredentials.Select(credentials => credentials.Key),
+ TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key),
+ ValidateLifetime = false,
+ ValidAudience = context.Issuer.AbsoluteUri,
+ ValidIssuer = context.Issuer.AbsoluteUri,
+ ValidTypes = new[] { JsonWebTokenTypes.LogoutRequest }
+ };
+
+ var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(token, parameters);
+ if (!result.IsValid)
+ {
+ context.Logger.LogError("The logout request was rejected because an unknown " +
+ "or invalid request_id parameter was specified.");
+
+ context.Reject(
+ error: Errors.InvalidRequest,
+ description: "The specified 'request_id' parameter is invalid.");
+
+ return;
+ }
+
+ using var document = JsonDocument.Parse(
+ Base64UrlEncoder.Decode(((JsonWebToken) result.SecurityToken).InnerToken.EncodedPayload));
+ if (document.RootElement.ValueKind != JsonValueKind.Object)
+ {
+ throw new InvalidOperationException("The logout request payload is malformed.");
+ }
+
+ foreach (var parameter in document.RootElement.EnumerateObject())
{
// Avoid overriding the current request parameters.
- if (context.Request.HasParameter(parameter.Key))
+ if (context.Request.HasParameter(parameter.Name))
{
continue;
}
- context.Request.SetParameter(parameter.Key, parameter.Value);
+ context.Request.SetParameter(parameter.Name, parameter.Value.Clone());
}
}
}
@@ -209,19 +239,32 @@ namespace OpenIddict.Server.Owin
context.Request.RequestId = Base64UrlEncoder.Encode(data);
// Store the serialized logout request parameters in the distributed cache.
- var stream = new MemoryStream();
- using (var writer = new BsonDataWriter(stream))
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(new SecurityTokenDescriptor
{
- writer.CloseOutput = false;
-
- var serializer = JsonSerializer.CreateDefault();
- serializer.Serialize(writer, context.Request);
- }
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.LogoutRequest
+ },
+ Audience = context.Issuer.AbsoluteUri,
+ Claims = context.Request.GetParameters().ToDictionary(
+ parameter => parameter.Key,
+ parameter => parameter.Value.Value),
+ Issuer = context.Issuer.AbsoluteUri,
+ SigningCredentials = context.Options.SigningCredentials.First(),
+ Subject = new ClaimsIdentity()
+ });
+
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ encryptingCredentials: context.Options.EncryptionCredentials.First(),
+ additionalHeaderClaims: new Dictionary
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.LogoutRequest
+ });
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached payloads.
- await _cache.SetAsync(Cache.LogoutRequest + context.Request.RequestId,
- stream.ToArray(), _options.CurrentValue.LogoutEndpointCachingPolicy);
+ await _cache.SetStringAsync(Cache.LogoutRequest + context.Request.RequestId,
+ token, _options.CurrentValue.AuthorizationEndpointCachingPolicy);
// Create a new GET logout request containing only the request_id parameter.
var address = WebUtilities.AddQueryString(
diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs
index 511d48ff..29cae2b6 100644
--- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs
+++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs
@@ -10,12 +10,13 @@ using System.Collections.Immutable;
using System.ComponentModel;
using System.IO;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.Owin;
using Microsoft.Owin.Security;
-using Newtonsoft.Json;
using OpenIddict.Abstractions;
using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
@@ -837,29 +838,27 @@ namespace OpenIddict.Server.Owin
context.Logger.LogInformation("The response was successfully returned as a JSON document: {Response}.", context.Response);
- using (var buffer = new MemoryStream())
- using (var writer = new JsonTextWriter(new StreamWriter(buffer)))
+ using var stream = new MemoryStream();
+ await JsonSerializer.SerializeAsync(stream, context.Response, new JsonSerializerOptions
{
- var serializer = JsonSerializer.CreateDefault();
- serializer.Serialize(writer, context.Response);
-
- writer.Flush();
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ });
- if (!string.IsNullOrEmpty(context.Response.Error))
- {
- // Note: when using basic authentication, returning an invalid_client error MUST result in
- // an unauthorized response but returning a 401 status code would invoke the previously
- // registered authentication middleware and potentially replace it by a 302 response.
- // To work around this OWIN/Katana limitation, a 400 response code is always returned.
- response.StatusCode = 400;
- }
+ if (!string.IsNullOrEmpty(context.Response.Error))
+ {
+ // Note: when using basic authentication, returning an invalid_client error MUST result in
+ // an unauthorized response but returning a 401 status code would invoke the previously
+ // registered authentication middleware and potentially replace it by a 302 response.
+ // To work around this OWIN/Katana limitation, a 400 response code is always returned.
+ response.StatusCode = 400;
+ }
- response.ContentLength = buffer.Length;
- response.ContentType = "application/json;charset=UTF-8";
+ response.ContentLength = stream.Length;
+ response.ContentType = "application/json;charset=UTF-8";
- buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
- await buffer.CopyToAsync(response.Body, 4096, response.Context.Request.CallCancelled);
- }
+ stream.Seek(offset: 0, loc: SeekOrigin.Begin);
+ await stream.CopyToAsync(response.Body, 4096, response.Context.Request.CallCancelled);
context.HandleRequest();
}
diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs b/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs
index 67a8622f..14ebe2a3 100644
--- a/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs
+++ b/src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs
@@ -8,7 +8,7 @@ using System;
using System.Collections.Generic;
using System.Security.Claims;
using JetBrains.Annotations;
-using Newtonsoft.Json.Linq;
+using System.Text.Json;
using OpenIddict.Abstractions;
namespace OpenIddict.Server
@@ -80,7 +80,7 @@ namespace OpenIddict.Server
/// Note: this value should only be populated if the "address"
/// scope was requested and accepted by the resource owner.
///
- public JObject Address { get; set; }
+ public JsonElement Address { get; set; }
///
/// Gets or sets the values used for the "aud" claim.
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
index c8e043e5..9ea6b02c 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
@@ -16,7 +16,6 @@ using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlerFilters;
-using Properties = OpenIddict.Server.OpenIddictServerConstants.Properties;
namespace OpenIddict.Server
{
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs
index 1f578fbe..6d56ceb6 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs
@@ -5,18 +5,18 @@
*/
using System;
-using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
+using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
@@ -1098,12 +1098,13 @@ namespace OpenIddict.Server
return;
}
- var keys = new JArray();
+ using var stream = new MemoryStream();
+ using var writer = new Utf8JsonWriter(stream);
+
+ writer.WriteStartArray();
foreach (var key in notification.Keys)
{
- var item = new JObject();
-
// Ensure a key type has been provided.
// See https://tools.ietf.org/html/rfc7517#section-4.1
if (string.IsNullOrEmpty(key.Kty))
@@ -1114,49 +1115,60 @@ namespace OpenIddict.Server
continue;
}
- // Create a dictionary associating the
- // JsonWebKey components with their values.
- var parameters = new Dictionary
- {
- [JsonWebKeyParameterNames.Kid] = key.Kid,
- [JsonWebKeyParameterNames.Use] = key.Use,
- [JsonWebKeyParameterNames.Kty] = key.Kty,
- [JsonWebKeyParameterNames.Alg] = key.Alg,
- [JsonWebKeyParameterNames.Crv] = key.Crv,
- [JsonWebKeyParameterNames.E] = key.E,
- [JsonWebKeyParameterNames.N] = key.N,
- [JsonWebKeyParameterNames.X] = key.X,
- [JsonWebKeyParameterNames.Y] = key.Y,
- [JsonWebKeyParameterNames.X5t] = key.X5t,
- [JsonWebKeyParameterNames.X5u] = key.X5u
- };
+ writer.WriteStartObject();
- foreach (var parameter in parameters)
+ if (!string.IsNullOrEmpty(key.Kid)) writer.WriteString(JsonWebKeyParameterNames.Kid, key.Kid);
+ if (!string.IsNullOrEmpty(key.Use)) writer.WriteString(JsonWebKeyParameterNames.Use, key.Use);
+ if (!string.IsNullOrEmpty(key.Kty)) writer.WriteString(JsonWebKeyParameterNames.Kty, key.Kty);
+ if (!string.IsNullOrEmpty(key.Alg)) writer.WriteString(JsonWebKeyParameterNames.Alg, key.Alg);
+ if (!string.IsNullOrEmpty(key.Crv)) writer.WriteString(JsonWebKeyParameterNames.Crv, key.Crv);
+ if (!string.IsNullOrEmpty(key.E)) writer.WriteString(JsonWebKeyParameterNames.E, key.E);
+ if (!string.IsNullOrEmpty(key.N)) writer.WriteString(JsonWebKeyParameterNames.N, key.N);
+ if (!string.IsNullOrEmpty(key.X)) writer.WriteString(JsonWebKeyParameterNames.X, key.X);
+ if (!string.IsNullOrEmpty(key.Y)) writer.WriteString(JsonWebKeyParameterNames.Y, key.Y);
+ if (!string.IsNullOrEmpty(key.X5t)) writer.WriteString(JsonWebKeyParameterNames.X5t, key.X5t);
+ if (!string.IsNullOrEmpty(key.X5u)) writer.WriteString(JsonWebKeyParameterNames.X5u, key.X5u);
+
+ if (key.KeyOps.Count != 0)
{
- if (!string.IsNullOrEmpty(parameter.Value))
+ writer.WritePropertyName(JsonWebKeyParameterNames.KeyOps);
+ writer.WriteStartArray();
+
+ for (var index = 0; index < key.KeyOps.Count; index++)
{
- item.Add(parameter.Key, parameter.Value);
+ writer.WriteStringValue(key.KeyOps[index]);
}
- }
- if (key.KeyOps.Count != 0)
- {
- item.Add(JsonWebKeyParameterNames.KeyOps, new JArray(key.KeyOps));
+ writer.WriteEndArray();
}
if (key.X5c.Count != 0)
{
- item.Add(JsonWebKeyParameterNames.X5c, new JArray(key.X5c));
+ writer.WritePropertyName(JsonWebKeyParameterNames.X5c);
+ writer.WriteStartArray();
+
+ for (var index = 0; index < key.X5c.Count; index++)
+ {
+ writer.WriteStringValue(key.X5c[index]);
+ }
+
+ writer.WriteEndArray();
}
- keys.Add(item);
+ writer.WriteEndObject();
}
+ writer.WriteEndArray();
+ writer.Flush();
+ stream.Seek(0L, SeekOrigin.Begin);
+
+ using var document = JsonDocument.Parse(stream);
+
// Note: AddParameter() is used here to ensure the mandatory "keys" node
// is returned to the caller, even if the key set doesn't expose any key.
// See https://tools.ietf.org/html/rfc7517#section-5 for more information.
var response = new OpenIddictResponse();
- response.AddParameter(Parameters.Keys, keys);
+ response.AddParameter(Parameters.Keys, document.RootElement.Clone());
context.Response = response;
}
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
index 2d2aa55d..b97b9a09 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
@@ -10,17 +10,17 @@ using System.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlerFilters;
-using Properties = OpenIddict.Server.OpenIddictServerConstants.Properties;
namespace OpenIddict.Server
{
@@ -306,7 +306,7 @@ namespace OpenIddict.Server
break;
default:
- response[Claims.Audience] = new JArray(notification.Audiences);
+ response[Claims.Audience] = notification.Audiences.ToArray();
break;
}
@@ -1120,7 +1120,12 @@ namespace OpenIddict.Server
// When multiple claims share the same type, retrieve the underlying
// JSON values and add everything to a new unique JSON array.
- _ => new JArray(claims.Select(claim => ConvertToParameter(claim).Value))
+ _ => JsonSerializer.Deserialize(JsonSerializer.Serialize(
+ claims.Select(claim => ConvertToParameter(claim).Value), new JsonSerializerOptions
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = false
+ }))
};
}
@@ -1132,8 +1137,8 @@ namespace OpenIddict.Server
ClaimValueTypes.Integer32 => int.Parse(claim.Value, CultureInfo.InvariantCulture),
ClaimValueTypes.Integer64 => long.Parse(claim.Value, CultureInfo.InvariantCulture),
- JsonClaimValueTypes.Json => JToken.Parse(claim.Value),
- JsonClaimValueTypes.JsonArray => JToken.Parse(claim.Value),
+ JsonClaimValueTypes.Json => JsonSerializer.Deserialize(claim.Value),
+ JsonClaimValueTypes.JsonArray => JsonSerializer.Deserialize(claim.Value),
_ => new OpenIddictParameter(claim.Value)
};
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
index f07a8a53..ce0e4e1f 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
@@ -6,7 +6,6 @@
using System;
using System.Collections.Immutable;
-using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -15,7 +14,6 @@ using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlerFilters;
-using Properties = OpenIddict.Server.OpenIddictServerConstants.Properties;
namespace OpenIddict.Server
{
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
index 9ad11f78..3c691a57 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
@@ -6,7 +6,6 @@
using System;
using System.Collections.Immutable;
-using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -15,7 +14,6 @@ using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlerFilters;
-using Properties = OpenIddict.Server.OpenIddictServerConstants.Properties;
namespace OpenIddict.Server
{
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs
index 23745267..b4b3f8d7 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs
@@ -7,16 +7,13 @@
using System;
using System.Collections.Immutable;
using System.Linq;
-using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
-using Newtonsoft.Json.Linq;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
-using Properties = OpenIddict.Server.OpenIddictServerConstants.Properties;
namespace OpenIddict.Server
{
@@ -281,7 +278,7 @@ namespace OpenIddict.Server
break;
default:
- response[Claims.Audience] = new JArray(notification.Audiences);
+ response[Claims.Audience] = notification.Audiences.ToArray();
break;
}
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
index 95f41642..50809be8 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
@@ -428,13 +428,41 @@ namespace OpenIddict.Server
// Attach the principal extracted from the token to the parent event context.
context.Principal = new ClaimsPrincipal(result.ClaimsIdentity);
+ // Store the token type as a special private claim.
+ context.Principal.SetClaim(Claims.Private.TokenUsage, ((JsonWebToken) result.SecurityToken).Typ switch
+ {
+ JsonWebTokenTypes.AccessToken => TokenUsages.AccessToken,
+ JsonWebTokenTypes.IdentityToken => TokenUsages.IdToken,
+
+ JsonWebTokenTypes.Private.AuthorizationCode => TokenUsages.AuthorizationCode,
+ JsonWebTokenTypes.Private.DeviceCode => TokenUsages.DeviceCode,
+ JsonWebTokenTypes.Private.RefreshToken => TokenUsages.RefreshToken,
+ JsonWebTokenTypes.Private.UserCode => TokenUsages.UserCode,
+
+ _ => throw new InvalidOperationException("The token type is not supported.")
+ });
+
context.Logger.LogTrace("The token '{Token}' was successfully validated and the following claims " +
"could be extracted: {Claims}.", context.Token, context.Principal.Claims);
async ValueTask ValidateTokenAsync(string token, string type)
{
var parameters = context.Options.TokenValidationParameters.Clone();
- parameters.PropertyBag = new Dictionary { [Claims.Private.TokenUsage] = type };
+ parameters.ValidTypes = new[]
+ {
+ type switch
+ {
+ TokenUsages.AccessToken => JsonWebTokenTypes.AccessToken,
+ TokenUsages.IdToken => JsonWebTokenTypes.IdentityToken,
+
+ TokenUsages.AuthorizationCode => JsonWebTokenTypes.Private.AuthorizationCode,
+ TokenUsages.DeviceCode => JsonWebTokenTypes.Private.DeviceCode,
+ TokenUsages.RefreshToken => JsonWebTokenTypes.Private.RefreshToken,
+ TokenUsages.UserCode => JsonWebTokenTypes.Private.UserCode,
+
+ _ => throw new InvalidOperationException("The token type is not supported.")
+ }
+ };
parameters.ValidIssuer = context.Issuer?.AbsoluteUri;
parameters.IssuerSigningKeys = type switch
@@ -2685,18 +2713,32 @@ namespace OpenIddict.Server
return;
}
- context.Response.AccessToken = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor
{
- Claims = new Dictionary { [Claims.Private.TokenUsage] = TokenUsages.AccessToken },
- EncryptingCredentials = context.Options.EncryptionCredentials.FirstOrDefault(
- credentials => credentials.Key is SymmetricSecurityKey),
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.AccessToken
+ },
Issuer = context.Issuer?.AbsoluteUri,
SigningCredentials = context.Options.SigningCredentials.FirstOrDefault(credentials =>
credentials.Key is SymmetricSecurityKey) ?? context.Options.SigningCredentials.First(),
Subject = (ClaimsIdentity) context.AccessTokenPrincipal.Identity
});
+ var credentials = context.Options.EncryptionCredentials.FirstOrDefault(
+ credentials => credentials.Key is SymmetricSecurityKey);
+ if (credentials != null)
+ {
+ token = context.Options.JsonWebTokenHandler.EncryptToken(
+ token, credentials, new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.AccessToken
+ });
+ }
+
+ context.Response.AccessToken = token;
+
context.Logger.LogTrace("The access token '{Identifier}' was successfully created: {Payload}. " +
"The principal used to create the token contained the following claims: {Claims}.",
context.AccessTokenPrincipal.GetClaim(Claims.JwtId),
@@ -2840,22 +2882,32 @@ namespace OpenIddict.Server
return;
}
- context.Response.Code = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor
{
- Claims = new Dictionary { [Claims.Private.TokenUsage] = TokenUsages.AuthorizationCode },
- EncryptingCredentials = context.Options.EncryptionCredentials.FirstOrDefault(
- credentials => credentials.Key is SymmetricSecurityKey),
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.AuthorizationCode
+ },
Issuer = context.Issuer?.AbsoluteUri,
SigningCredentials = context.Options.SigningCredentials.FirstOrDefault(credentials =>
credentials.Key is SymmetricSecurityKey) ?? context.Options.SigningCredentials.First(),
Subject = (ClaimsIdentity) context.AuthorizationCodePrincipal.Identity
});
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ context.Options.EncryptionCredentials.First(),
+ new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.AuthorizationCode
+ });
+
+ context.Response.Code = token;
+
context.Logger.LogTrace("The authorization code '{Identifier}' was successfully created: {Payload}. " +
"The principal used to create the token contained the following claims: {Claims}.",
- context.AuthorizationCodePrincipal.GetClaim(Claims.JwtId),
- context.Response.Code, context.AuthorizationCodePrincipal.Claims);
+ context.AuthorizationCodePrincipal.GetClaim(Claims.JwtId), token,
+ context.AuthorizationCodePrincipal.Claims);
}
}
@@ -2994,22 +3046,32 @@ namespace OpenIddict.Server
return;
}
- context.Response.DeviceCode = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor
{
- Claims = new Dictionary { [Claims.Private.TokenUsage] = TokenUsages.DeviceCode },
- EncryptingCredentials = context.Options.EncryptionCredentials.FirstOrDefault(
- credentials => credentials.Key is SymmetricSecurityKey),
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.DeviceCode
+ },
Issuer = context.Issuer?.AbsoluteUri,
SigningCredentials = context.Options.SigningCredentials.FirstOrDefault(credentials =>
credentials.Key is SymmetricSecurityKey) ?? context.Options.SigningCredentials.First(),
Subject = (ClaimsIdentity) context.DeviceCodePrincipal.Identity
});
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ context.Options.EncryptionCredentials.First(),
+ new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.DeviceCode
+ });
+
+ context.Response.DeviceCode = token;
+
context.Logger.LogTrace("The device code '{Identifier}' was successfully created: {Payload}. " +
"The principal used to create the token contained the following claims: {Claims}.",
- context.DeviceCodePrincipal.GetClaim(Claims.JwtId),
- context.Response.DeviceCode, context.DeviceCodePrincipal.Claims);
+ context.DeviceCodePrincipal.GetClaim(Claims.JwtId), token,
+ context.DeviceCodePrincipal.Claims);
}
}
@@ -3249,21 +3311,32 @@ namespace OpenIddict.Server
return;
}
- context.Response.RefreshToken = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor
{
- Claims = new Dictionary { [Claims.Private.TokenUsage] = TokenUsages.RefreshToken },
- EncryptingCredentials = context.Options.EncryptionCredentials[0],
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.RefreshToken
+ },
Issuer = context.Issuer?.AbsoluteUri,
SigningCredentials = context.Options.SigningCredentials.FirstOrDefault(credentials =>
credentials.Key is SymmetricSecurityKey) ?? context.Options.SigningCredentials.First(),
Subject = (ClaimsIdentity) context.RefreshTokenPrincipal.Identity
});
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ context.Options.EncryptionCredentials.First(),
+ new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.RefreshToken
+ });
+
+ context.Response.RefreshToken = token;
+
context.Logger.LogTrace("The refresh token '{Identifier}' was successfully created: {Payload}. " +
"The principal used to create the token contained the following claims: {Claims}.",
- context.RefreshTokenPrincipal.GetClaim(Claims.JwtId),
- context.Response.RefreshToken, context.RefreshTokenPrincipal.Claims);
+ context.RefreshTokenPrincipal.GetClaim(Claims.JwtId), token,
+ context.RefreshTokenPrincipal.Claims);
}
}
@@ -3442,22 +3515,32 @@ namespace OpenIddict.Server
return;
}
- context.Response.UserCode = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
+ var token = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor
{
- Claims = new Dictionary { [Claims.Private.TokenUsage] = TokenUsages.UserCode },
- EncryptingCredentials = context.Options.EncryptionCredentials.FirstOrDefault(
- credentials => credentials.Key is SymmetricSecurityKey),
+ AdditionalHeaderClaims = new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.UserCode
+ },
Issuer = context.Issuer?.AbsoluteUri,
SigningCredentials = context.Options.SigningCredentials.FirstOrDefault(credentials =>
credentials.Key is SymmetricSecurityKey) ?? context.Options.SigningCredentials.First(),
Subject = (ClaimsIdentity) context.UserCodePrincipal.Identity
});
+ token = context.Options.JsonWebTokenHandler.EncryptToken(token,
+ context.Options.EncryptionCredentials.First(),
+ new Dictionary(StringComparer.Ordinal)
+ {
+ [JwtHeaderParameterNames.Typ] = JsonWebTokenTypes.Private.UserCode
+ });
+
+ context.Response.UserCode = token;
+
context.Logger.LogTrace("The user code '{Identifier}' was successfully created: {Payload}. " +
"The principal used to create the token contained the following claims: {Claims}.",
- context.UserCodePrincipal.GetClaim(Claims.JwtId),
- context.Response.UserCode, context.UserCodePrincipal.Claims);
+ context.UserCodePrincipal.GetClaim(Claims.JwtId), token,
+ context.UserCodePrincipal.Claims);
}
}
@@ -3743,7 +3826,10 @@ namespace OpenIddict.Server
context.Response.IdToken = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor
{
- Claims = new Dictionary