From 0b629b3dbd263ef50071fe18549888e41624ff25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sun, 13 Sep 2020 05:58:24 +0200 Subject: [PATCH] Replace JsonSerializer.Serialize() by Utf8JsonWriter and JsonSerializer.Deserialize() by JsonDocument --- .../Primitives/OpenIddictExtensions.cs | 58 +++++-- .../Primitives/OpenIddictParameter.cs | 56 +++++-- ...enIddictEntityFrameworkApplicationStore.cs | 147 ++++++++++++++++-- ...IddictEntityFrameworkAuthorizationStore.cs | 57 ++++++- .../OpenIddictEntityFrameworkScopeStore.cs | 77 +++++++-- .../OpenIddictEntityFrameworkTokenStore.cs | 30 +++- ...dictEntityFrameworkCoreApplicationStore.cs | 147 ++++++++++++++++-- ...ctEntityFrameworkCoreAuthorizationStore.cs | 57 ++++++- ...OpenIddictEntityFrameworkCoreScopeStore.cs | 77 +++++++-- ...OpenIddictEntityFrameworkCoreTokenStore.cs | 30 +++- .../OpenIddictMongoDbApplicationStore.cs | 41 +++-- .../OpenIddictMongoDbAuthorizationStore.cs | 35 ++++- .../Stores/OpenIddictMongoDbScopeStore.cs | 35 ++++- .../Stores/OpenIddictMongoDbTokenStore.cs | 33 +++- .../OpenIddictServerAspNetCoreHandlers.cs | 9 +- ...OpenIddictServerDataProtectionFormatter.cs | 36 ++++- .../OpenIddictServerOwinHandlers.cs | 9 +- .../OpenIddictServerHandlers.Introspection.cs | 65 +++++++- .../OpenIddictValidationAspNetCoreHandlers.cs | 9 +- ...IddictValidationDataProtectionFormatter.cs | 18 ++- .../OpenIddictValidationOwinHandlers.cs | 9 +- .../OpenIddict.Server.IntegrationTests.csproj | 1 + .../OpenIddictServerIntegrationTestClient.cs | 9 +- 23 files changed, 897 insertions(+), 148 deletions(-) diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs index f48fb945..4eae7074 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs @@ -9,8 +9,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Linq; using System.Security.Claims; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using Microsoft.Extensions.Primitives; @@ -517,9 +519,21 @@ namespace OpenIddict.Abstractions return ImmutableArray.Create(); } - return JsonSerializer.Deserialize>(destinations) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToImmutableArray(); + using var document = JsonDocument.Parse(destinations); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + var value = element.GetString(); + if (builder.Contains(value, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + + builder.Add(value); + } + + return builder.ToImmutable(); } /// @@ -546,8 +560,18 @@ namespace OpenIddict.Abstractions return false; } - return JsonSerializer.Deserialize>(destinations) - .Contains(destination, StringComparer.OrdinalIgnoreCase); + using var document = JsonDocument.Parse(destinations); + + foreach (var element in document.RootElement.EnumerateArray()) + { + var value = element.GetString(); + if (string.Equals(value, destination, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; } /// @@ -574,12 +598,24 @@ namespace OpenIddict.Abstractions throw new ArgumentException(SR.GetResourceString(SR.ID1181), nameof(destinations)); } - claim.Properties[Properties.Destinations] = - JsonSerializer.Serialize(destinations.Distinct(StringComparer.OrdinalIgnoreCase), new JsonSerializerOptions - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - }); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = false + }); + + writer.WriteStartArray(); + + foreach (var destination in destinations.Distinct(StringComparer.OrdinalIgnoreCase)) + { + writer.WriteStringValue(destination); + } + + writer.WriteEndArray(); + writer.Flush(); + + claim.Properties[Properties.Destinations] = Encoding.UTF8.GetString(stream.ToArray()); return claim; } diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs index 6268b6d4..3582fef3 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs @@ -9,7 +9,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Globalization; +using System.IO; using System.Linq; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using SR = OpenIddict.Abstractions.OpenIddictResources; @@ -624,18 +626,10 @@ namespace OpenIddict.Abstractions // to a JSON object or array), try to deserialize it to get a JsonElement instance. string value when value.Length != 0 && (value[0] == '{' || value[0] == '[') => DeserializeElement(value) ?? - DeserializeElement(JsonSerializer.Serialize(value, new JsonSerializerOptions - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - })) ?? default, + DeserializeElement(SerializeObject(value)) ?? default, // Otherwise, serialize it to get a JsonElement instance. - var value => DeserializeElement(JsonSerializer.Serialize(value, new JsonSerializerOptions - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - })) ?? default + var value => DeserializeElement(SerializeObject(value)) ?? default }; static JsonElement? DeserializeElement(string value) @@ -651,6 +645,48 @@ namespace OpenIddict.Abstractions return null; } } + + static string SerializeObject(object instance) + { + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = false + }); + + switch (instance) + { + case bool value: + writer.WriteBooleanValue(value); + break; + + case long value: + writer.WriteNumberValue(value); + break; + + case string value: + writer.WriteStringValue(value); + break; + + case string?[] value: + writer.WriteStartArray(); + + for (var index = 0; index < value.Length; index++) + { + writer.WriteStringValue(value[index]); + } + + writer.WriteEndArray(); + break; + + default: throw new InvalidOperationException(SR.GetResourceString(SR.ID1193)); + } + + writer.Flush(); + + return Encoding.UTF8.GetString(stream.ToArray()); + } } /// diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs index 00d17fc8..88189ff4 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs @@ -399,8 +399,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.DisplayNames) - .ToImmutableDictionary(name => CultureInfo.GetCultureInfo(name.Key), name => name.Value); + using var document = JsonDocument.Parse(application.DisplayNames); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + } + + return builder.ToImmutable(); }); return new ValueTask>(names); @@ -438,7 +445,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.Permissions); + using var document = JsonDocument.Parse(application.Permissions); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(permissions); @@ -465,7 +480,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.PostLogoutRedirectUris); + using var document = JsonDocument.Parse(application.PostLogoutRedirectUris); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(addresses); @@ -492,7 +515,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.Properties); + using var document = JsonDocument.Parse(application.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -519,7 +550,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.RedirectUris); + using var document = JsonDocument.Parse(application.RedirectUris); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(addresses); @@ -546,7 +585,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.Requirements); + using var document = JsonDocument.Parse(application.Requirements); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(requirements); @@ -718,12 +765,25 @@ namespace OpenIddict.EntityFramework return default; } - application.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var permission in permissions) + { + writer.WriteStringValue(permission); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.Permissions = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -743,12 +803,25 @@ namespace OpenIddict.EntityFramework return default; } - application.PostLogoutRedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var address in addresses) + { + writer.WriteStringValue(address); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.PostLogoutRedirectUris = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -768,12 +841,26 @@ namespace OpenIddict.EntityFramework return default; } - application.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + application.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -793,12 +880,25 @@ namespace OpenIddict.EntityFramework return default; } - application.RedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var address in addresses) + { + writer.WriteStringValue(address); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.RedirectUris = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -817,12 +917,25 @@ namespace OpenIddict.EntityFramework return default; } - application.Requirements = JsonSerializer.Serialize(requirements, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var requirement in requirements) + { + writer.WriteStringValue(requirement); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.Requirements = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs index cbaaa811..d7e1ebf2 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs @@ -11,8 +11,10 @@ using System.ComponentModel; using System.Data; using System.Data.Entity; using System.Data.Entity.Infrastructure; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -452,7 +454,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(authorization.Properties); + using var document = JsonDocument.Parse(authorization.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -479,7 +489,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(authorization.Scopes); + using var document = JsonDocument.Parse(authorization.Scopes); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(scopes); @@ -707,12 +725,26 @@ namespace OpenIddict.EntityFramework return default; } - authorization.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + authorization.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -732,12 +764,25 @@ namespace OpenIddict.EntityFramework return default; } - authorization.Scopes = JsonSerializer.Serialize(scopes, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var scope in scopes) + { + writer.WriteStringValue(scope); + } + + writer.WriteEndArray(); + writer.Flush(); + + authorization.Scopes = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs index 5d7144bc..9997775d 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs @@ -260,8 +260,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.Descriptions) - .ToImmutableDictionary(description => CultureInfo.GetCultureInfo(description.Key), description => description.Value); + using var document = JsonDocument.Parse(scope.Descriptions); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + } + + return builder.ToImmutable(); }); return new ValueTask>(descriptions); @@ -299,8 +306,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.DisplayNames) - .ToImmutableDictionary(name => CultureInfo.GetCultureInfo(name.Key), name => name.Value); + using var document = JsonDocument.Parse(scope.DisplayNames); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + } + + return builder.ToImmutable(); }); return new ValueTask>(names); @@ -349,7 +363,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.Properties); + using var document = JsonDocument.Parse(scope.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -376,7 +398,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.Resources); + using var document = JsonDocument.Parse(scope.Resources); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(resources); @@ -561,12 +591,26 @@ namespace OpenIddict.EntityFramework return default; } - scope.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + scope.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -585,12 +629,25 @@ namespace OpenIddict.EntityFramework return default; } - scope.Resources = JsonSerializer.Serialize(resources, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var resource in resources) + { + writer.WriteStringValue(resource); + } + + writer.WriteEndArray(); + writer.Flush(); + + scope.Resources = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs index aa4b6350..e7b76620 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs @@ -11,7 +11,9 @@ using System.ComponentModel; using System.Data; using System.Data.Entity; using System.Data.Entity.Infrastructure; +using System.IO; using System.Linq; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -444,7 +446,15 @@ namespace OpenIddict.EntityFramework entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(token.Properties); + using var document = JsonDocument.Parse(token.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -746,12 +756,26 @@ namespace OpenIddict.EntityFramework return default; } - token.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + token.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs index f3441323..ef4e5f9e 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs @@ -450,8 +450,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.DisplayNames) - .ToImmutableDictionary(name => CultureInfo.GetCultureInfo(name.Key), name => name.Value); + using var document = JsonDocument.Parse(application.DisplayNames); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + } + + return builder.ToImmutable(); }); return new ValueTask>(names); @@ -489,7 +496,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.Permissions); + using var document = JsonDocument.Parse(application.Permissions); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(permissions); @@ -516,7 +531,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.PostLogoutRedirectUris); + using var document = JsonDocument.Parse(application.PostLogoutRedirectUris); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(addresses); @@ -543,7 +566,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.Properties); + using var document = JsonDocument.Parse(application.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -570,7 +601,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.RedirectUris); + using var document = JsonDocument.Parse(application.RedirectUris); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(addresses); @@ -597,7 +636,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(application.Requirements); + using var document = JsonDocument.Parse(application.Requirements); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(requirements); @@ -768,12 +815,25 @@ namespace OpenIddict.EntityFrameworkCore return default; } - application.Permissions = JsonSerializer.Serialize(permissions, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var permission in permissions) + { + writer.WriteStringValue(permission); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.Permissions = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -793,12 +853,25 @@ namespace OpenIddict.EntityFrameworkCore return default; } - application.PostLogoutRedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var address in addresses) + { + writer.WriteStringValue(address); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.PostLogoutRedirectUris = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -818,12 +891,26 @@ namespace OpenIddict.EntityFrameworkCore return default; } - application.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + application.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -843,12 +930,25 @@ namespace OpenIddict.EntityFrameworkCore return default; } - application.RedirectUris = JsonSerializer.Serialize(addresses, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var address in addresses) + { + writer.WriteStringValue(address); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.RedirectUris = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -867,12 +967,25 @@ namespace OpenIddict.EntityFrameworkCore return default; } - application.Requirements = JsonSerializer.Serialize(requirements, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var requirement in requirements) + { + writer.WriteStringValue(requirement); + } + + writer.WriteEndArray(); + writer.Flush(); + + application.Requirements = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs index dceabfce..79759f3d 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs @@ -9,8 +9,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Data; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -520,7 +522,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(authorization.Properties); + using var document = JsonDocument.Parse(authorization.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -547,7 +557,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(authorization.Scopes); + using var document = JsonDocument.Parse(authorization.Scopes); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(scopes); @@ -793,12 +811,26 @@ namespace OpenIddict.EntityFrameworkCore return default; } - authorization.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + authorization.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -818,12 +850,25 @@ namespace OpenIddict.EntityFrameworkCore return default; } - authorization.Scopes = JsonSerializer.Serialize(scopes, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var scope in scopes) + { + writer.WriteStringValue(scope); + } + + writer.WriteEndArray(); + writer.Flush(); + + authorization.Scopes = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs index c2796ed3..2db4effc 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs @@ -276,8 +276,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.Descriptions) - .ToImmutableDictionary(description => CultureInfo.GetCultureInfo(description.Key), description => description.Value); + using var document = JsonDocument.Parse(scope.Descriptions); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + } + + return builder.ToImmutable(); }); return new ValueTask>(descriptions); @@ -315,8 +322,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.DisplayNames) - .ToImmutableDictionary(name => CultureInfo.GetCultureInfo(name.Key), name => name.Value); + using var document = JsonDocument.Parse(scope.DisplayNames); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + } + + return builder.ToImmutable(); }); return new ValueTask>(names); @@ -365,7 +379,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.Properties); + using var document = JsonDocument.Parse(scope.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -392,7 +414,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(scope.Resources); + using var document = JsonDocument.Parse(scope.Resources); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); }); return new ValueTask>(resources); @@ -577,12 +607,26 @@ namespace OpenIddict.EntityFrameworkCore return default; } - scope.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + scope.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } @@ -601,12 +645,25 @@ namespace OpenIddict.EntityFrameworkCore return default; } - scope.Resources = JsonSerializer.Serialize(resources, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartArray(); + + foreach (var resource in resources) + { + writer.WriteStringValue(resource); + } + + writer.WriteEndArray(); + writer.Flush(); + + scope.Resources = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreTokenStore.cs index dc8a7f40..0a8559fc 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreTokenStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreTokenStore.cs @@ -9,7 +9,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Data; +using System.IO; using System.Linq; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -496,7 +498,15 @@ namespace OpenIddict.EntityFrameworkCore entry.SetPriority(CacheItemPriority.High) .SetSlidingExpiration(TimeSpan.FromMinutes(1)); - return JsonSerializer.Deserialize>(token.Properties); + using var document = JsonDocument.Parse(token.Properties); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return builder.ToImmutable(); }); return new ValueTask>(properties); @@ -821,12 +831,26 @@ namespace OpenIddict.EntityFrameworkCore return default; } - token.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + token.Properties = Encoding.UTF8.GetString(stream.ToArray()); + return default; } diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbApplicationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbApplicationStore.cs index b547228e..16d708dd 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbApplicationStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbApplicationStore.cs @@ -8,8 +8,10 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -336,8 +338,15 @@ namespace OpenIddict.MongoDb return new ValueTask>(ImmutableDictionary.Create()); } - return new ValueTask>( - JsonSerializer.Deserialize>(application.Properties.ToJson())); + using var document = JsonDocument.Parse(application.Properties.ToJson()); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return new ValueTask>(builder.ToImmutable()); } /// @@ -536,7 +545,7 @@ namespace OpenIddict.MongoDb return default; } - application.Permissions = permissions.ToArray(); + application.Permissions = permissions; return default; } @@ -557,7 +566,7 @@ namespace OpenIddict.MongoDb return default; } - application.PostLogoutRedirectUris = addresses.ToArray(); + application.PostLogoutRedirectUris = addresses; return default; } @@ -578,11 +587,25 @@ namespace OpenIddict.MongoDb return default; } - application.Properties = BsonDocument.Parse(JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - })); + Indented = false + }); + + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + application.Properties = BsonDocument.Parse(Encoding.UTF8.GetString(stream.ToArray())); return default; } @@ -603,7 +626,7 @@ namespace OpenIddict.MongoDb return default; } - application.RedirectUris = addresses.ToArray(); + application.RedirectUris = addresses; return default; } @@ -624,7 +647,7 @@ namespace OpenIddict.MongoDb return default; } - application.Requirements = requirements.ToArray(); + application.Requirements = requirements; return default; } diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbAuthorizationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbAuthorizationStore.cs index 0b605e10..6354efcb 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbAuthorizationStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbAuthorizationStore.cs @@ -7,8 +7,10 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -396,8 +398,15 @@ namespace OpenIddict.MongoDb return new ValueTask>(ImmutableDictionary.Create()); } - return new ValueTask>( - JsonSerializer.Deserialize>(authorization.Properties.ToJson())); + using var document = JsonDocument.Parse(authorization.Properties.ToJson()); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return new ValueTask>(builder.ToImmutable()); } /// @@ -615,11 +624,25 @@ namespace OpenIddict.MongoDb return default; } - authorization.Properties = BsonDocument.Parse(JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - })); + Indented = false + }); + + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + authorization.Properties = BsonDocument.Parse(Encoding.UTF8.GetString(stream.ToArray())); return default; } @@ -640,7 +663,7 @@ namespace OpenIddict.MongoDb return default; } - authorization.Scopes = scopes.ToArray(); + authorization.Scopes = scopes; return default; } diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbScopeStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbScopeStore.cs index 2edb4795..9dc91b9d 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbScopeStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbScopeStore.cs @@ -8,8 +8,10 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -285,8 +287,15 @@ namespace OpenIddict.MongoDb return new ValueTask>(ImmutableDictionary.Create()); } - return new ValueTask>( - JsonSerializer.Deserialize>(scope.Properties.ToJson())); + using var document = JsonDocument.Parse(scope.Properties.ToJson()); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return new ValueTask>(builder.ToImmutable()); } /// @@ -452,11 +461,25 @@ namespace OpenIddict.MongoDb return default; } - scope.Properties = BsonDocument.Parse(JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - })); + Indented = false + }); + + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + scope.Properties = BsonDocument.Parse(Encoding.UTF8.GetString(stream.ToArray())); return default; } @@ -476,7 +499,7 @@ namespace OpenIddict.MongoDb return default; } - scope.Resources = resources.ToArray(); + scope.Resources = resources; return default; } diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs index 070a8921..7116af99 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs @@ -7,8 +7,10 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -417,8 +419,15 @@ namespace OpenIddict.MongoDb return new ValueTask>(ImmutableDictionary.Create()); } - return new ValueTask>( - JsonSerializer.Deserialize>(token.Properties.ToJson())); + using var document = JsonDocument.Parse(token.Properties.ToJson()); + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var property in document.RootElement.EnumerateObject()) + { + builder[property.Name] = property.Value; + } + + return new ValueTask>(builder.ToImmutable()); } /// @@ -677,11 +686,25 @@ namespace OpenIddict.MongoDb return default; } - token.Properties = BsonDocument.Parse(JsonSerializer.Serialize(properties, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - })); + Indented = false + }); + + writer.WriteStartObject(); + + foreach (var property in properties) + { + writer.WritePropertyName(property.Key); + property.Value.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + token.Properties = BsonDocument.Parse(Encoding.UTF8.GetString(stream.ToArray())); return default; } diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs index 06edc14e..38fdd9fc 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs @@ -1062,6 +1062,8 @@ namespace OpenIddict.Server.AspNetCore throw new ArgumentNullException(nameof(context)); } + Debug.Assert(context.Transaction.Response is not null, SR.GetResourceString(SR.ID5007)); + // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetHttpRequest()?.HttpContext.Response; @@ -1073,12 +1075,15 @@ namespace OpenIddict.Server.AspNetCore context.Logger.LogInformation(SR.GetResourceString(SR.ID7142), context.Transaction.Response); using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, context.Transaction.Response, new JsonSerializerOptions + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = true }); + context.Transaction.Response.WriteTo(writer); + writer.Flush(); + response.ContentLength = stream.Length; response.ContentType = "application/json;charset=UTF-8"; diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs index b16072b0..94b6ad14 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs @@ -10,6 +10,7 @@ using System.Collections.Immutable; using System.IO; using System.Linq; using System.Security.Claims; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using OpenIddict.Abstractions; @@ -180,8 +181,22 @@ namespace OpenIddict.Server.DataProtection => properties.TryGetValue(name, out var value) ? value : null; static ImmutableArray GetArrayProperty(IReadOnlyDictionary properties, string name) - => properties.TryGetValue(name, out var value) ? - JsonSerializer.Deserialize>(value) : ImmutableArray.Create(); + { + if (properties.TryGetValue(name, out var value)) + { + using var document = JsonDocument.Parse(value); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); + } + + return ImmutableArray.Create(); + } } public void WriteToken(BinaryWriter writer, ClaimsPrincipal principal) @@ -381,11 +396,24 @@ namespace OpenIddict.Server.DataProtection else { - properties[name] = JsonSerializer.Serialize(values, new JsonSerializerOptions + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = false }); + + writer.WriteStartArray(); + + foreach (var value in values) + { + writer.WriteStringValue(value); + } + + writer.WriteEndArray(); + writer.Flush(); + + properties[name] = Encoding.UTF8.GetString(stream.ToArray()); } } } diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs index 6fe0e0cc..777c10eb 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs @@ -999,6 +999,8 @@ namespace OpenIddict.Server.Owin throw new ArgumentNullException(nameof(context)); } + Debug.Assert(context.Transaction.Response is not null, SR.GetResourceString(SR.ID5007)); + // This handler only applies to OWIN requests. If The OWIN request cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetOwinRequest()?.Context.Response; @@ -1010,12 +1012,15 @@ namespace OpenIddict.Server.Owin context.Logger.LogInformation(SR.GetResourceString(SR.ID7142), context.Transaction.Response); using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, context.Transaction.Response, new JsonSerializerOptions + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = true }); + context.Transaction.Response.WriteTo(writer); + writer.Flush(); + response.ContentLength = stream.Length; response.ContentType = "application/json;charset=UTF-8"; diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs index cbc366b3..b31e941c 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs @@ -5,11 +5,15 @@ */ using System; +using System.Collections; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Linq; using System.Security.Claims; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading.Tasks; @@ -998,8 +1002,8 @@ namespace OpenIddict.Server continue; } - var claims = grouping.ToArray(); - context.Claims[type] = claims.Length switch + var claims = grouping.ToList(); + context.Claims[type] = claims.Count switch { // When there's only one claim with the same type, directly // convert the claim using the specified claim value type. @@ -1007,12 +1011,7 @@ namespace OpenIddict.Server // When multiple claims share the same type, retrieve the underlying // JSON values and add everything to a new unique JSON array. - _ => DeserializeElement(JsonSerializer.Serialize( - claims.Select(claim => ConvertToParameter(claim).Value), new JsonSerializerOptions - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false - })) + _ => DeserializeElement(SerializeClaims(claims)) }; } @@ -1035,6 +1034,56 @@ namespace OpenIddict.Server using var document = JsonDocument.Parse(value); return document.RootElement.Clone(); } + + static string SerializeClaims(IReadOnlyList claims) + { + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = false + }); + + writer.WriteStartArray(); + + for (var index = 0; index < claims.Count; index++) + { + var claim = claims[index]; + + switch (claim.ValueType) + { + case ClaimValueTypes.Boolean: + writer.WriteBooleanValue(bool.Parse(claim.Value)); + break; + + case ClaimValueTypes.Integer: + case ClaimValueTypes.Integer32: + writer.WriteNumberValue(int.Parse(claim.Value, CultureInfo.InvariantCulture)); + break; + + case ClaimValueTypes.Integer64: + writer.WriteNumberValue(long.Parse(claim.Value, CultureInfo.InvariantCulture)); + break; + + case JsonClaimValueTypes.Json: + case JsonClaimValueTypes.JsonArray: + using (var document = JsonDocument.Parse(claim.Value)) + { + document.WriteTo(writer); + } + break; + + default: + writer.WriteStringValue(claim.Value); + break; + } + } + + writer.WriteEndArray(); + writer.Flush(); + + return Encoding.UTF8.GetString(stream.ToArray()); + } } } diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs index cfe5f213..295a2df2 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs @@ -608,6 +608,8 @@ namespace OpenIddict.Validation.AspNetCore throw new ArgumentNullException(nameof(context)); } + Debug.Assert(context.Transaction.Response is not null, SR.GetResourceString(SR.ID5007)); + // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetHttpRequest()?.HttpContext.Response; @@ -619,12 +621,15 @@ namespace OpenIddict.Validation.AspNetCore context.Logger.LogInformation(SR.GetResourceString(SR.ID7142), context.Transaction.Response); using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, context.Transaction.Response, new JsonSerializerOptions + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = true }); + context.Transaction.Response.WriteTo(writer); + writer.Flush(); + response.ContentLength = stream.Length; response.ContentType = "application/json;charset=UTF-8"; diff --git a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs index af4fb29d..8d204700 100644 --- a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs +++ b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs @@ -178,8 +178,22 @@ namespace OpenIddict.Validation.DataProtection => properties.TryGetValue(name, out var value) ? value : null; static ImmutableArray GetArrayProperty(IReadOnlyDictionary properties, string name) - => properties.TryGetValue(name, out var value) ? - JsonSerializer.Deserialize>(value) : ImmutableArray.Create(); + { + if (properties.TryGetValue(name, out var value)) + { + using var document = JsonDocument.Parse(value); + var builder = ImmutableArray.CreateBuilder(document.RootElement.GetArrayLength()); + + foreach (var element in document.RootElement.EnumerateArray()) + { + builder.Add(element.GetString()); + } + + return builder.ToImmutable(); + } + + return ImmutableArray.Create(); + } } } } diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs index 02640b97..15e82c44 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs @@ -615,6 +615,8 @@ namespace OpenIddict.Validation.Owin throw new ArgumentNullException(nameof(context)); } + Debug.Assert(context.Transaction.Response is not null, SR.GetResourceString(SR.ID5007)); + // This handler only applies to OWIN requests. If The OWIN request cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetOwinRequest()?.Context.Response; @@ -626,12 +628,15 @@ namespace OpenIddict.Validation.Owin context.Logger.LogInformation(SR.GetResourceString(SR.ID7142), context.Transaction.Response); using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, context.Transaction.Response, new JsonSerializerOptions + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = false + Indented = true }); + context.Transaction.Response.WriteTo(writer); + writer.Flush(); + response.ContentLength = stream.Length; response.ContentType = "application/json;charset=UTF-8"; diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddict.Server.IntegrationTests.csproj b/test/OpenIddict.Server.IntegrationTests/OpenIddict.Server.IntegrationTests.csproj index fbc6a480..c8f6de40 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddict.Server.IntegrationTests.csproj +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddict.Server.IntegrationTests.csproj @@ -20,6 +20,7 @@ + diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs index cf7ff5e6..b6ca921a 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs @@ -9,9 +9,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Json; using System.Text; using System.Text.Encodings.Web; -using System.Text.Json; using System.Threading.Tasks; using AngleSharp.Html.Parser; using Microsoft.Extensions.Primitives; @@ -421,12 +421,7 @@ namespace OpenIddict.Server.IntegrationTests else if (string.Equals(message.Content?.Headers?.ContentType?.MediaType, "application/json", StringComparison.OrdinalIgnoreCase)) { - // Note: this test client is only used with OpenIddict's ASP.NET Core or OWIN hosts, - // that always return their HTTP responses encoded using UTF-8. As such, the stream - // returned by ReadAsStreamAsync() is always assumed to contain UTF-8 encoded payloads. - using var stream = await message.Content.ReadAsStreamAsync(); - - return await JsonSerializer.DeserializeAsync(stream); + return await message.Content.ReadFromJsonAsync(); } else if (string.Equals(message.Content?.Headers?.ContentType?.MediaType, "text/html", StringComparison.OrdinalIgnoreCase))