From b19fdffea4ec77fbdaaa49fe1d41e49b85bea052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Tue, 17 Nov 2020 16:53:29 +0100 Subject: [PATCH] React to nullable annotations changes in .NET 5.0 and update OpenIddictParameter.ToString() to use the invariant culture --- .../Helpers/OpenIddictHelpers.cs | 2 +- .../OpenIddictResources.resx | 9 ++- .../Primitives/OpenIddictExtensions.cs | 16 ++++- .../Primitives/OpenIddictParameter.cs | 11 +-- ...enIddictEntityFrameworkApplicationStore.cs | 40 +++++++++-- ...IddictEntityFrameworkAuthorizationStore.cs | 8 ++- .../OpenIddictEntityFrameworkScopeStore.cs | 24 ++++++- ...dictEntityFrameworkCoreApplicationStore.cs | 40 +++++++++-- ...ctEntityFrameworkCoreAuthorizationStore.cs | 8 ++- ...OpenIddictEntityFrameworkCoreScopeStore.cs | 24 ++++++- .../OpenIddictServerAspNetCoreHandler.cs | 2 +- ...OpenIddictServerDataProtectionFormatter.cs | 10 ++- .../OpenIddictServerOwinHandler.cs | 2 +- .../OpenIddictServerHandlers.Discovery.cs | 8 +-- .../OpenIddictServerHandlers.Exchange.cs | 9 +-- .../OpenIddictServerHandlers.Introspection.cs | 8 +-- .../OpenIddictServerHandlers.Revocation.cs | 7 +- .../OpenIddictServerHandlers.Userinfo.cs | 5 +- .../OpenIddictServerHandlers.cs | 71 +++++++++---------- .../OpenIddictServerOptions.cs | 2 +- .../OpenIddictValidationAspNetCoreHandler.cs | 3 +- ...IddictValidationDataProtectionFormatter.cs | 8 ++- .../OpenIddictValidationOwinHandler.cs | 2 +- ...enIddictValidationSystemNetHttpHandlers.cs | 34 +++++---- .../OpenIddictValidationBuilder.cs | 2 +- ...nIddictValidationHandlers.Introspection.cs | 10 ++- .../OpenIddictValidationHandlers.cs | 8 +-- .../OpenIddictValidationService.cs | 4 +- .../Primitives/OpenIddictExtensionsTests.cs | 38 ++++++---- .../Primitives/OpenIddictMessageTests.cs | 2 +- .../Primitives/OpenIddictParameterTests.cs | 20 +++++- .../OpenIddictServerIntegrationTestClient.cs | 21 ++++-- ...nIddictServerIntegrationTests.Discovery.cs | 6 +- 33 files changed, 326 insertions(+), 138 deletions(-) diff --git a/shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs b/shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs index 65624a72..b90a5b46 100644 --- a/shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs +++ b/shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs @@ -16,7 +16,7 @@ namespace OpenIddict.Extensions /// The type to introspect. /// The generic type definition. /// A instance if the base type was found, null otherwise. - public static Type FindGenericBaseType(Type type, Type definition) + public static Type? FindGenericBaseType(Type type, Type definition) => FindGenericBaseTypes(type, definition).FirstOrDefault(); /// diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index 8e0bb501..755354b0 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -152,7 +152,7 @@ When implementing custom token deserialization, a 'oi_tkn_typ' claim containing A sign-in response cannot be returned from this endpoint. - The specified principal doesn't contain any claims-based identity. + The specified principal is null or doesn't contain a claims-based identity. Make sure that 'ClaimsPrincipal.Identity' is not null. @@ -184,7 +184,7 @@ Alternatively, you can disable the built-in database-based server features by en An unknown error occurred while creating a token entry. - A token entry cannot be created from a null principal. + A token entry cannot be created from a null principal or from a principal containing a null or invalid identity. The token entry cannot be found in the database. @@ -1113,6 +1113,9 @@ To register the OpenIddict core services, reference the 'OpenIddict.Core' packag Endpoint addresses must be unique across endpoints. + + The specified principal doesn't contain a valid claims-based identity. + The security token is missing. @@ -1483,7 +1486,7 @@ To register the OpenIddict core services, reference the 'OpenIddict.Core' packag ECDsa.ExportParameters() shouldn't return an unnamed curve. - The principal shouldn't be null at this point. + The principal and its attached identity shouldn't be null at this point. The response shouldn't be null at this point. diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs index db688500..40136ed0 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs @@ -525,7 +525,7 @@ namespace OpenIddict.Abstractions foreach (var element in document.RootElement.EnumerateArray()) { var value = element.GetString(); - if (builder.Contains(value, StringComparer.OrdinalIgnoreCase)) + if (string.IsNullOrEmpty(value) || builder.Contains(value, StringComparer.OrdinalIgnoreCase)) { continue; } @@ -1061,6 +1061,11 @@ namespace OpenIddict.Abstractions throw new ArgumentNullException(nameof(principal)); } + if (principal.Identity is not ClaimsIdentity identity) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0286), nameof(principal)); + } + if (string.IsNullOrEmpty(type)) { throw new ArgumentException(SR.GetResourceString(SR.ID0184), nameof(type)); @@ -1070,7 +1075,7 @@ namespace OpenIddict.Abstractions if (!string.IsNullOrEmpty(value)) { - ((ClaimsIdentity) principal.Identity).AddClaim(type, value); + identity.AddClaim(type, value); } return principal; @@ -1119,6 +1124,11 @@ namespace OpenIddict.Abstractions throw new ArgumentNullException(nameof(principal)); } + if (principal.Identity is not ClaimsIdentity identity) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0286), nameof(principal)); + } + if (string.IsNullOrEmpty(type)) { throw new ArgumentException(SR.GetResourceString(SR.ID0184), nameof(type)); @@ -1128,7 +1138,7 @@ namespace OpenIddict.Abstractions foreach (var value in values.Distinct(StringComparer.Ordinal)) { - ((ClaimsIdentity) principal.Identity).AddClaim(type, value); + identity.AddClaim(type, value); } return principal; diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs index 020d84bd..fcdeabc1 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs @@ -258,7 +258,7 @@ namespace OpenIddict.Abstractions return value.GetInt64().GetHashCode(); case JsonValueKind.String: - return value.GetString().GetHashCode(); + return value.GetString()!.GetHashCode(); case JsonValueKind.Array: { @@ -420,12 +420,15 @@ namespace OpenIddict.Abstractions { null => string.Empty, - string value => value, + bool value => value ? bool.TrueString : bool.FalseString, + long value => value.ToString(CultureInfo.InvariantCulture), + + string value => value, string?[] value => string.Join(", ", value), JsonElement value => value.ToString(), - var value => value.ToString() + _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0194)) }; /// @@ -799,7 +802,7 @@ namespace OpenIddict.Abstractions static string?[]? CreateArray(JsonElement value) { - var array = new string[value.GetArrayLength()]; + var array = new string?[value.GetArrayLength()]; using var enumerator = value.EnumerateArray(); for (var index = 0; enumerator.MoveNext(); index++) diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs index 5265b957..7a8eda0a 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkApplicationStore.cs @@ -404,7 +404,13 @@ namespace OpenIddict.EntityFramework foreach (var property in document.RootElement.EnumerateObject()) { - builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + var value = property.Value.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder[CultureInfo.GetCultureInfo(property.Name)] = value; } return builder.ToImmutable(); @@ -450,7 +456,13 @@ namespace OpenIddict.EntityFramework foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); @@ -485,7 +497,13 @@ namespace OpenIddict.EntityFramework foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); @@ -555,7 +573,13 @@ namespace OpenIddict.EntityFramework foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); @@ -590,7 +614,13 @@ namespace OpenIddict.EntityFramework foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs index 77b7ad1e..e4e32187 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs @@ -500,7 +500,13 @@ namespace OpenIddict.EntityFramework foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs index 025502af..11fb520e 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkScopeStore.cs @@ -265,7 +265,13 @@ namespace OpenIddict.EntityFramework foreach (var property in document.RootElement.EnumerateObject()) { - builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + var value = property.Value.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder[CultureInfo.GetCultureInfo(property.Name)] = value; } return builder.ToImmutable(); @@ -311,7 +317,13 @@ namespace OpenIddict.EntityFramework foreach (var property in document.RootElement.EnumerateObject()) { - builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + var value = property.Value.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder[CultureInfo.GetCultureInfo(property.Name)] = value; } return builder.ToImmutable(); @@ -403,7 +415,13 @@ namespace OpenIddict.EntityFramework foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs index be6e82ca..2ce6182e 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreApplicationStore.cs @@ -448,7 +448,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var property in document.RootElement.EnumerateObject()) { - builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + var value = property.Value.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder[CultureInfo.GetCultureInfo(property.Name)] = value; } return builder.ToImmutable(); @@ -494,7 +500,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); @@ -529,7 +541,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); @@ -599,7 +617,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); @@ -634,7 +658,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs index 3b25725e..643053c1 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs @@ -568,7 +568,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs index 57513223..df777826 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreScopeStore.cs @@ -281,7 +281,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var property in document.RootElement.EnumerateObject()) { - builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + var value = property.Value.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder[CultureInfo.GetCultureInfo(property.Name)] = value; } return builder.ToImmutable(); @@ -327,7 +333,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var property in document.RootElement.EnumerateObject()) { - builder[CultureInfo.GetCultureInfo(property.Name)] = property.Value.GetString(); + var value = property.Value.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder[CultureInfo.GetCultureInfo(property.Name)] = value; } return builder.ToImmutable(); @@ -419,7 +431,13 @@ namespace OpenIddict.EntityFrameworkCore foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var value = element.GetString(); + if (string.IsNullOrEmpty(value)) + { + continue; + } + + builder.Add(value); } return builder.ToImmutable(); diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs index c9c76c4d..2d214c47 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs @@ -155,7 +155,7 @@ namespace OpenIddict.Server.AspNetCore else { - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); Debug.Assert(!string.IsNullOrEmpty(context.Principal.GetTokenType()), SR.GetResourceString(SR.ID4009)); Debug.Assert(!string.IsNullOrEmpty(context.Token), SR.GetResourceString(SR.ID4010)); diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs index 9113ce8c..2f02cb8c 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs @@ -189,7 +189,13 @@ namespace OpenIddict.Server.DataProtection foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var item = element.GetString(); + if (string.IsNullOrEmpty(item)) + { + continue; + } + + builder.Add(item); } return builder.ToImmutable(); @@ -264,7 +270,7 @@ namespace OpenIddict.Server.DataProtection Claims.Private.TokenId or Claims.Private.UserCodeLifetime)); - Write(writer, principal.Identity.AuthenticationType, principal, properties); + Write(writer, principal.Identity?.AuthenticationType, principal, properties); writer.Flush(); // Note: the following local methods closely matches the logic used by ASP.NET Core's diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs index 4eedbb83..ca5bc475 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs @@ -167,7 +167,7 @@ namespace OpenIddict.Server.Owin else { - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); Debug.Assert(!string.IsNullOrEmpty(context.Principal.GetTokenType()), SR.GetResourceString(SR.ID4009)); Debug.Assert(!string.IsNullOrEmpty(context.Token), SR.GetResourceString(SR.ID4010)); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs index 649aabd3..dea942c3 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs @@ -371,7 +371,7 @@ namespace OpenIddict.Server return default; - static Uri? GetEndpointAbsoluteUri(Uri? issuer, Uri endpoint) + static Uri? GetEndpointAbsoluteUri(Uri? issuer, Uri? endpoint) { // If the endpoint is disabled (i.e a null address is specified), return null. if (endpoint is null) @@ -1248,8 +1248,8 @@ namespace OpenIddict.Server // Warning: on .NET Framework 4.x and .NET Core 2.1, exported ECParameters generally have // a null OID value attached. To work around this limitation, both the friendly names and // the raw OID value are compared to determine whether the curve is of the specified type. - string.Equals(parameters.Curve.Oid.Value, curve.Oid.Value, StringComparison.Ordinal) || - string.Equals(parameters.Curve.Oid.FriendlyName, curve.Oid.FriendlyName, StringComparison.Ordinal); + string.Equals(parameters.Curve.Oid?.Value, curve.Oid?.Value, StringComparison.Ordinal) || + string.Equals(parameters.Curve.Oid?.FriendlyName, curve.Oid?.FriendlyName, StringComparison.Ordinal); #endif static byte[] GetCertificateHash(X509Certificate2 certificate, HashAlgorithmName algorithm) @@ -1257,7 +1257,7 @@ namespace OpenIddict.Server #if SUPPORTS_CERTIFICATE_HASHING_WITH_SPECIFIED_ALGORITHM return certificate.GetCertHash(algorithm); #else - using var hash = CryptoConfig.CreateFromName(algorithm.Name) as HashAlgorithm; + using var hash = CryptoConfig.CreateFromName(algorithm.Name!) as HashAlgorithm; if (hash is null || hash is KeyedHashAlgorithm) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0217)); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs index 70177f76..e29a4341 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; @@ -1331,7 +1332,7 @@ namespace OpenIddict.Server return default; } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var presenters = context.Principal.GetPresenters(); if (presenters.IsDefaultOrEmpty) @@ -1415,7 +1416,7 @@ namespace OpenIddict.Server return default; } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Validate the redirect_uri sent by the client application as part of this token request. // Note: for pure OAuth 2.0 requests, redirect_uri is only mandatory if the authorization request @@ -1484,7 +1485,7 @@ namespace OpenIddict.Server return default; } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Note: the ValidateProofKeyForCodeExchangeRequirement handler (invoked earlier) ensures // a code_verifier is specified if the proof key for code exchange requirement was enforced @@ -1605,7 +1606,7 @@ namespace OpenIddict.Server return default; } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // When an explicit scope parameter has been included in the token request // but was missing from the initial request, the request MUST be rejected. diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs index c6696e44..ea19f4e5 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs @@ -742,7 +742,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken) && !context.Principal.HasTokenType(TokenTypeHints.RefreshToken)) @@ -789,7 +789,7 @@ namespace OpenIddict.Server } Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId)); - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // When the introspected token is an access token, the caller must be listed either as a presenter // (i.e the party the token was issued to) or as an audience (i.e a resource server/API). @@ -885,7 +885,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); context.TokenId = context.Principal.GetClaim(Claims.JwtId); context.TokenUsage = context.Principal.GetTokenType(); @@ -945,7 +945,7 @@ namespace OpenIddict.Server } Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), SR.FormatID4000(Parameters.ClientId)); - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Don't return application-specific claims if the token is not an access token. if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken)) diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs index 5fea8966..0b8614fa 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; @@ -684,7 +685,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken) && !context.Principal.HasTokenType(TokenTypeHints.RefreshToken)) @@ -731,7 +732,7 @@ namespace OpenIddict.Server } Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId)); - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // When the revoked token is an access token, the caller must be listed either as a presenter // (i.e the party the token was issued to) or as an audience (i.e a resource server/API). @@ -836,7 +837,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Extract the token identifier from the authentication principal. var identifier = context.Principal.GetTokenId(); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs index 1524fe5d..5b18ecd1 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; @@ -461,7 +462,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Note: when receiving an access token, its audiences list cannot be used for the "aud" claim // as the client application is not the intented audience but only an authorized presenter. @@ -495,7 +496,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); context.Subject = context.Principal.GetClaim(Claims.Subject); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index 99c34402..53f1e462 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -823,7 +823,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Extract the token identifier from the authentication principal. // If no token identifier can be found, this indicates that the token @@ -1030,7 +1030,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var identifier = context.Principal.GetAuthorizationId(); if (string.IsNullOrEmpty(identifier)) @@ -1089,7 +1089,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Don't validate the lifetime of id_tokens used as id_token_hints. if (context.EndpointType is OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Logout) @@ -1355,8 +1355,6 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); - if (context.EndpointType is not (OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Device or OpenIddictServerEndpointType.Token or @@ -1365,7 +1363,7 @@ namespace OpenIddict.Server throw new InvalidOperationException(SR.GetResourceString(SR.ID0010)); } - if (context.Principal.Identity is null) + if (context.Principal is not { Identity: ClaimsIdentity }) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0011)); } @@ -1384,18 +1382,19 @@ namespace OpenIddict.Server { throw new InvalidOperationException(SR.GetResourceString(SR.ID0013)); } - - return default; } - if (!context.Principal.Identity.IsAuthenticated) + else { - throw new InvalidOperationException(SR.GetResourceString(SR.ID0014)); - } + if (!context.Principal.Identity.IsAuthenticated) + { + throw new InvalidOperationException(SR.GetResourceString(SR.ID0014)); + } - if (string.IsNullOrEmpty(context.Principal.GetClaim(Claims.Subject))) - { - throw new InvalidOperationException(SR.GetResourceString(SR.ID0015)); + if (string.IsNullOrEmpty(context.Principal.GetClaim(Claims.Subject))) + { + throw new InvalidOperationException(SR.GetResourceString(SR.ID0015)); + } } return default; @@ -1425,7 +1424,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); switch (context.EndpointType) { @@ -1497,7 +1496,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Always include the "openid" scope when the developer doesn't explicitly call SetScopes. // Note: the application is allowed to specify a different "scopes": in this case, @@ -1534,7 +1533,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Add the validated client_id to the list of authorized presenters, // unless the presenters were explicitly set by the developer. @@ -1570,7 +1569,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // When a "resources" property cannot be found in the ticket, infer it from the "audiences" property. if (context.Principal.HasAudience() && !context.Principal.HasResource()) @@ -1609,7 +1608,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); (context.GenerateAccessToken, context.IncludeAccessToken) = context.EndpointType switch { @@ -1723,7 +1722,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // If no authorization code, device code or refresh token is returned, don't create an authorization. if (!context.GenerateAuthorizationCode && !context.GenerateDeviceCode && !context.GenerateRefreshToken) @@ -1809,7 +1808,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Create a new principal containing only the filtered claims. // Actors identities are also filtered (delegation scenarios). @@ -1927,7 +1926,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Create a new principal containing only the filtered claims. // Actors identities are also filtered (delegation scenarios). @@ -2012,7 +2011,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Note: a device code principal is produced when a device code is included in the response or when a // device code entry is replaced when processing a sign-in response sent to the verification endpoint. @@ -2092,7 +2091,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Create a new principal containing only the filtered claims. // Actors identities are also filtered (delegation scenarios). @@ -2176,7 +2175,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Replace the principal by a new one containing only the filtered claims. // Actors identities are also filtered (delegation scenarios). @@ -2289,7 +2288,7 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Create a new principal containing only the filtered claims. // Actors identities are also filtered (delegation scenarios). @@ -2379,7 +2378,7 @@ namespace OpenIddict.Server default: return; } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // Extract the token identifier from the authentication principal. // If no token identifier can be found, this indicates that the token has no backing database entry. @@ -2526,7 +2525,7 @@ namespace OpenIddict.Server Claims.Private.Scope or Claims.Private.TokenType)); - if (principal is null) + if (principal is null or { Identity: not ClaimsIdentity }) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0020)); } @@ -2785,7 +2784,7 @@ namespace OpenIddict.Server Claims.Private.ExpirationDate or Claims.Private.TokenType)); - if (principal is null) + if (principal is null or { Identity: not ClaimsIdentity }) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0022)); } @@ -3031,7 +3030,7 @@ namespace OpenIddict.Server Claims.Private.ExpirationDate or Claims.Private.TokenType)); - if (principal is null) + if (principal is null or { Identity: not ClaimsIdentity }) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0022)); } @@ -3197,7 +3196,7 @@ namespace OpenIddict.Server return; } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var principal = context.DeviceCodePrincipal; if (principal is null) @@ -3361,7 +3360,7 @@ namespace OpenIddict.Server Claims.Private.ExpirationDate or Claims.Private.TokenType)); - if (principal is null) + if (principal is null or { Identity: not ClaimsIdentity }) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0022)); } @@ -3644,7 +3643,7 @@ namespace OpenIddict.Server Claims.Private.ExpirationDate or Claims.Private.TokenType)); - if (principal is null) + if (principal is null or { Identity: not ClaimsIdentity }) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0022)); } @@ -3900,7 +3899,7 @@ namespace OpenIddict.Server } }; - hash = CryptoConfig.CreateFromName(algorithm.Name) as HashAlgorithm; + hash = CryptoConfig.CreateFromName(algorithm.Name!) as HashAlgorithm; } return hash; @@ -4034,7 +4033,7 @@ namespace OpenIddict.Server Claims.Private.ExpirationDate or Claims.Private.TokenType)); - if (principal is null) + if (principal is null or { Identity: not ClaimsIdentity }) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0022)); } @@ -4227,7 +4226,7 @@ namespace OpenIddict.Server return default; - static Uri? GetEndpointAbsoluteUri(Uri? issuer, Uri endpoint) + static Uri? GetEndpointAbsoluteUri(Uri? issuer, Uri? endpoint) { // If the endpoint is disabled (i.e a null address is specified), return null. if (endpoint is null) diff --git a/src/OpenIddict.Server/OpenIddictServerOptions.cs b/src/OpenIddict.Server/OpenIddictServerOptions.cs index 5ecacdf6..60582ee5 100644 --- a/src/OpenIddict.Server/OpenIddictServerOptions.cs +++ b/src/OpenIddict.Server/OpenIddictServerOptions.cs @@ -80,7 +80,7 @@ namespace OpenIddict.Server /// /// Gets the absolute and relative URIs associated to the cryptography endpoint. /// - public List CryptographyEndpointUris { get; } = new List + public List CryptographyEndpointUris { get; } = new() { new Uri("/.well-known/jwks", UriKind.Relative) }; diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs index 927b29cc..1835ecd8 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; @@ -152,7 +153,7 @@ namespace OpenIddict.Validation.AspNetCore else { - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); Debug.Assert(!string.IsNullOrEmpty(context.Principal.GetTokenType()), SR.GetResourceString(SR.ID4009)); Debug.Assert(!string.IsNullOrEmpty(context.Token), SR.GetResourceString(SR.ID4010)); diff --git a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs index 8d204700..a27d1c96 100644 --- a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs +++ b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs @@ -186,7 +186,13 @@ namespace OpenIddict.Validation.DataProtection foreach (var element in document.RootElement.EnumerateArray()) { - builder.Add(element.GetString()); + var item = element.GetString(); + if (string.IsNullOrEmpty(item)) + { + continue; + } + + builder.Add(item); } return builder.ToImmutable(); diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs index 53940633..67c3885b 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs @@ -164,7 +164,7 @@ namespace OpenIddict.Validation.Owin else { - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); Debug.Assert(!string.IsNullOrEmpty(context.Principal.GetTokenType()), SR.GetResourceString(SR.ID4009)); Debug.Assert(!string.IsNullOrEmpty(context.Token), SR.GetResourceString(SR.ID4010)); diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs index 60c70a70..67d701b2 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs @@ -123,6 +123,7 @@ namespace OpenIddict.Validation.SystemNetHttp throw new ArgumentNullException(nameof(context)); } + Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008)); Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008)); // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, @@ -133,22 +134,25 @@ namespace OpenIddict.Validation.SystemNetHttp throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); } - // Note: System.Net.Http doesn't expose convenient methods allowing to create - // query strings from existing key/value pairs. To work around this limitation, - // a FormUrlEncodedContent is instantiated and used to manually create the URL. - using var content = new FormUrlEncodedContent( - from parameter in context.Transaction.Request.GetParameters() - let values = (string[]?) parameter.Value - where values is not null - from value in values - select new KeyValuePair(parameter.Key, value)); - - var builder = new UriBuilder(request.RequestUri) + if (request.RequestUri is not null) { - Query = await content.ReadAsStringAsync() - }; - - request.RequestUri = builder.Uri; + // Note: System.Net.Http doesn't expose convenient methods allowing to create + // query strings from existing key/value pairs. To work around this limitation, + // a FormUrlEncodedContent is instantiated and used to manually create the URL. + using var content = new FormUrlEncodedContent( + from parameter in context.Transaction.Request.GetParameters() + let values = (string[]?) parameter.Value + where values is not null + from value in values + select new KeyValuePair(parameter.Key, value)); + + var builder = new UriBuilder(request.RequestUri) + { + Query = await content.ReadAsStringAsync() + }; + + request.RequestUri = builder.Uri; + } } } diff --git a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs index 7106d22f..f8d77ef1 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs @@ -330,7 +330,7 @@ namespace Microsoft.Extensions.DependencyInjection return AddEncryptionCertificate(certificate); - static X509Certificate2 GetCertificate(StoreLocation location, string thumbprint) + static X509Certificate2? GetCertificate(StoreLocation location, string thumbprint) { using var store = new X509Store(StoreName.My, location); store.Open(OpenFlags.ReadOnly); diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs index ca6dbd2d..04112d5d 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs @@ -384,13 +384,19 @@ namespace OpenIddict.Validation case JsonElement { ValueKind: JsonValueKind.Array } value: foreach (var element in value.EnumerateArray()) { - identity.AddClaim(new Claim(parameter.Key, element.ToString(), + var item = element.GetString(); + if (string.IsNullOrEmpty(item)) + { + continue; + } + + identity.AddClaim(new Claim(parameter.Key, item, GetClaimValueType(value.ValueKind), issuer, issuer, identity)); } break; case JsonElement value: - identity.AddClaim(new Claim(parameter.Key, value.ToString(), + identity.AddClaim(new Claim(parameter.Key, value.ToString()!, GetClaimValueType(value.ValueKind), issuer, issuer, identity)); break; diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs index c851c97e..8cbcc902 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs @@ -641,7 +641,7 @@ namespace OpenIddict.Validation throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var date = context.Principal.GetExpirationDate(); if (date.HasValue && date.Value < DateTimeOffset.UtcNow) @@ -683,7 +683,7 @@ namespace OpenIddict.Validation throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); // If no explicit audience has been configured, // skip the default audience validation. @@ -755,7 +755,7 @@ namespace OpenIddict.Validation throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var identifier = context.Principal.GetTokenId(); if (string.IsNullOrEmpty(identifier)) @@ -818,7 +818,7 @@ namespace OpenIddict.Validation throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var identifier = context.Principal.GetAuthorizationId(); if (string.IsNullOrEmpty(identifier)) diff --git a/src/OpenIddict.Validation/OpenIddictValidationService.cs b/src/OpenIddict.Validation/OpenIddictValidationService.cs index fb80bad3..d5dfbcbb 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationService.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationService.cs @@ -458,7 +458,7 @@ namespace OpenIddict.Validation context.Error, context.ErrorDescription, context.ErrorUri); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); return context.Principal; } @@ -521,7 +521,7 @@ namespace OpenIddict.Validation context.Error, context.ErrorDescription, context.ErrorUri); } - Debug.Assert(context.Principal is not null, SR.GetResourceString(SR.ID4006)); + Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); return context.Principal; } diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs index b90389c1..62a911ee 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs @@ -1169,9 +1169,9 @@ namespace OpenIddict.Abstractions.Tests.Primitives principal.SetDestinations(destinations.ToImmutable()); // Assert - Assert.Equal(@"[""access_token"",""id_token""]", principal.FindFirst(Claims.Name).Properties[Properties.Destinations]); - Assert.Equal(@"[""id_token""]", principal.FindFirst(Claims.Email).Properties[Properties.Destinations]); - Assert.DoesNotContain(Properties.Destinations, principal.FindFirst(Claims.Nonce).Properties); + Assert.Equal(@"[""access_token"",""id_token""]", principal.FindFirst(Claims.Name)!.Properties[Properties.Destinations]); + Assert.Equal(@"[""id_token""]", principal.FindFirst(Claims.Email)!.Properties[Properties.Destinations]); + Assert.DoesNotContain(Properties.Destinations, principal.FindFirst(Claims.Nonce)!.Properties); } [Theory] @@ -1241,7 +1241,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives // Assert Assert.Single(clone.Claims); Assert.Null(clone.FindFirst(Claims.Subject)); - Assert.Equal("Bob le Bricoleur", clone.FindFirst(Claims.Name).Value); + Assert.Equal("Bob le Bricoleur", clone.FindFirst(Claims.Name)!.Value); } [Fact] @@ -1259,9 +1259,9 @@ namespace OpenIddict.Abstractions.Tests.Primitives var clone = identity.Clone(claim => claim.Type == Claims.Name); // Assert - Assert.Single(clone.Actor.Claims); + Assert.Single(clone.Actor!.Claims); Assert.Null(clone.Actor.FindFirst(Claims.Subject)); - Assert.Equal("Bob le Bricoleur", clone.Actor.FindFirst(Claims.Name).Value); + Assert.Equal("Bob le Bricoleur", clone.Actor.FindFirst(Claims.Name)!.Value); } [Fact] @@ -1280,7 +1280,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives // Assert Assert.Single(clone.Claims); Assert.Null(clone.FindFirst(Claims.Subject)); - Assert.Equal("Bob le Bricoleur", clone.FindFirst(Claims.Name).Value); + Assert.Equal("Bob le Bricoleur", clone.FindFirst(Claims.Name)!.Value); } [Fact] @@ -1308,7 +1308,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives identity.AddClaim(Claims.Name, "Bob le Bricoleur"); // Assert - Assert.Equal("Bob le Bricoleur", identity.FindFirst(Claims.Name).Value); + Assert.Equal("Bob le Bricoleur", identity.FindFirst(Claims.Name)!.Value); } [Theory] @@ -1324,7 +1324,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives // Act identity.AddClaim(Claims.Name, "Bob le Bricoleur", ImmutableArray.Create(destinations)); - var claim = identity.FindFirst(Claims.Name); + var claim = identity.FindFirst(Claims.Name)!; // Assert Assert.Equal("Bob le Bricoleur", claim.Value); @@ -1344,7 +1344,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives // Act identity.AddClaim(Claims.Name, "Bob le Bricoleur", destinations); - var claim = identity.FindFirst(Claims.Name); + var claim = identity.FindFirst(Claims.Name)!; // Assert Assert.Equal("Bob le Bricoleur", claim.Value); @@ -2422,13 +2422,27 @@ namespace OpenIddict.Abstractions.Tests.Primitives Assert.Equal("principal", exception.ParamName); } + [Fact] + public void SetClaim_ThrowsAnExceptionForNullIdentity() + { + // Arrange + var principal = new ClaimsPrincipal(); + + // Act and assert + var exception = Assert.Throws(() => principal.SetClaim("type", "value")); + + Assert.Equal("principal", exception.ParamName); + Assert.StartsWith(SR.GetResourceString(SR.ID0286), exception.Message); + } + [Theory] [InlineData(null)] [InlineData("")] - public void SetClaim_ThrowsAnExceptionForNullOrEmptyProperty(string type) + public void SetClaim_ThrowsAnExceptionForNullOrEmptyType(string type) { // Arrange - var principal = new ClaimsPrincipal(); + var identity = new ClaimsIdentity(); + var principal = new ClaimsPrincipal(identity); // Act and assert var exception = Assert.Throws(() => principal.SetClaim(type, "value")); diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs index d192f982..de1f3cc9 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs @@ -446,7 +446,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives ""logo_uri"": ""https://client.example.org/logo.png"", ""jwks_uri"": ""https://client.example.org/my_public_keys.jwks"", ""example_extension_parameter"": ""example_value"" -}"); +}")!; var options = new JsonSerializerOptions { diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs index 2cef4d87..b91e8b90 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs @@ -38,7 +38,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives } [Fact] - public void Count_ReturnsZeroForInteger() + public void Count_ReturnsZeroForLongValue() { // Arrange var parameter = new OpenIddictParameter(42); @@ -614,6 +614,24 @@ namespace OpenIddict.Abstractions.Tests.Primitives Assert.Empty(parameter.ToString()); } + [Fact] + public void ToString_ReturnsBooleanValue() + { + // Arrange, act and assert + Assert.Equal(bool.TrueString, new OpenIddictParameter(true).ToString()); + Assert.Equal(bool.FalseString, new OpenIddictParameter(false).ToString()); + } + + [Fact] + public void ToString_ReturnsLongValue() + { + // Arrange + var parameter = new OpenIddictParameter(42); + + // Act and assert + Assert.Equal("42", parameter.ToString()); + } + [Fact] public void ToString_ReturnsStringValue() { diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs index a294445f..78b93f55 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs @@ -256,14 +256,14 @@ namespace OpenIddict.Server.IntegrationTests // Note: a dictionary is deliberately not used here to allow multiple parameters with the // same name to be specified. While initially not allowed by the core OAuth2 specification, // this is required for derived drafts like the OAuth2 token exchange specification. - var parameters = new List>(); + var parameters = new List>(); foreach (var parameter in request.GetParameters()) { // If the parameter is null or empty, send an empty value. if (OpenIddictParameter.IsNullOrEmpty(parameter.Value)) { - parameters.Add(new KeyValuePair(parameter.Key, string.Empty)); + parameters.Add(new KeyValuePair(parameter.Key, string.Empty)); continue; } @@ -276,7 +276,7 @@ namespace OpenIddict.Server.IntegrationTests foreach (var value in values) { - parameters.Add(new KeyValuePair(parameter.Key, value)); + parameters.Add(new KeyValuePair(parameter.Key, value)); } } @@ -286,19 +286,28 @@ namespace OpenIddict.Server.IntegrationTests foreach (var parameter in parameters) { + if (string.IsNullOrEmpty(parameter.Key)) + { + continue; + } + if (builder.Length != 0) { builder.Append('&'); } builder.Append(UrlEncoder.Default.Encode(parameter.Key)); - builder.Append('='); - builder.Append(UrlEncoder.Default.Encode(parameter.Value)); + + if (!string.IsNullOrEmpty(parameter.Value)) + { + builder.Append('='); + builder.Append(UrlEncoder.Default.Encode(parameter.Value)); + } } if (!uri.IsAbsoluteUri) { - uri = new Uri(HttpClient.BaseAddress, uri); + uri = new Uri(HttpClient.BaseAddress!, uri); } uri = new UriBuilder(uri) { Query = builder.ToString() }.Uri; diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs index 6fb41f55..2b8b38a0 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Discovery.cs @@ -238,8 +238,7 @@ namespace OpenIddict.Server.IntegrationTests var response = await client.GetAsync("/.well-known/openid-configuration"); // Assert - Assert.Equal(client.HttpClient.BaseAddress.AbsoluteUri, - (string?) response[Metadata.Issuer]); + Assert.Equal(client.HttpClient.BaseAddress!.AbsoluteUri, (string?) response[Metadata.Issuer]); } [Fact] @@ -257,8 +256,7 @@ namespace OpenIddict.Server.IntegrationTests var response = await client.GetAsync("/.well-known/openid-configuration"); // Assert - Assert.Equal("https://www.fabrikam.com/", - (string?) response[Metadata.Issuer]); + Assert.Equal("https://www.fabrikam.com/", (string?) response[Metadata.Issuer]); } [Fact]