diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs index 9c5f849e..ce3e2bec 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs @@ -29,7 +29,7 @@ namespace OpenIddict.Abstractions /// Extracts the authentication context class values from an . /// /// The instance. - public static ImmutableHashSet GetAcrValues([NotNull] this OpenIddictRequest request) + public static ImmutableArray GetAcrValues([NotNull] this OpenIddictRequest request) { if (request == null) { @@ -38,17 +38,17 @@ namespace OpenIddict.Abstractions if (string.IsNullOrEmpty(request.AcrValues)) { - return ImmutableHashSet.Create(StringComparer.Ordinal); + return ImmutableArray.Create(); } - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, GetValues(request.AcrValues, Separators.Space)); + return GetValues(request.AcrValues, Separators.Space).Distinct(StringComparer.Ordinal).ToImmutableArray(); } /// /// Extracts the response types from an . /// /// The instance. - public static ImmutableHashSet GetResponseTypes([NotNull] this OpenIddictRequest request) + public static ImmutableArray GetResponseTypes([NotNull] this OpenIddictRequest request) { if (request == null) { @@ -57,17 +57,17 @@ namespace OpenIddict.Abstractions if (string.IsNullOrEmpty(request.ResponseType)) { - return ImmutableHashSet.Create(StringComparer.Ordinal); + return ImmutableArray.Create(); } - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, GetValues(request.ResponseType, Separators.Space)); + return GetValues(request.ResponseType, Separators.Space).Distinct(StringComparer.Ordinal).ToImmutableArray(); } /// /// Extracts the scopes from an . /// /// The instance. - public static ImmutableHashSet GetScopes([NotNull] this OpenIddictRequest request) + public static ImmutableArray GetScopes([NotNull] this OpenIddictRequest request) { if (request == null) { @@ -76,10 +76,10 @@ namespace OpenIddict.Abstractions if (string.IsNullOrEmpty(request.Scope)) { - return ImmutableHashSet.Create(StringComparer.Ordinal); + return ImmutableArray.Create(); } - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, GetValues(request.Scope, Separators.Space)); + return GetValues(request.Scope, Separators.Space).Distinct(StringComparer.Ordinal).ToImmutableArray(); } /// @@ -524,7 +524,7 @@ namespace OpenIddict.Abstractions /// /// The instance. /// The destinations associated with the claim. - public static ImmutableHashSet GetDestinations([NotNull] this Claim claim) + public static ImmutableArray GetDestinations([NotNull] this Claim claim) { if (claim == null) { @@ -535,10 +535,10 @@ namespace OpenIddict.Abstractions if (string.IsNullOrEmpty(destinations)) { - return ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase); + return ImmutableArray.Create(); } - return ImmutableHashSet.CreateRange(StringComparer.OrdinalIgnoreCase, JArray.Parse(destinations).Values()); + return JArray.Parse(destinations).Values().Distinct(StringComparer.OrdinalIgnoreCase).ToImmutableArray(); } /// @@ -558,7 +558,14 @@ namespace OpenIddict.Abstractions throw new ArgumentException("The destination cannot be null or empty.", nameof(destination)); } - return GetDestinations(claim).Contains(destination, StringComparer.OrdinalIgnoreCase); + claim.Properties.TryGetValue(Properties.Destinations, out string destinations); + + if (string.IsNullOrEmpty(destinations)) + { + return false; + } + + return JArray.Parse(destinations).Values().Contains(destination, StringComparer.OrdinalIgnoreCase); } /// @@ -566,14 +573,14 @@ namespace OpenIddict.Abstractions /// /// The instance. /// The destinations. - public static Claim SetDestinations([NotNull] this Claim claim, IEnumerable destinations) + public static Claim SetDestinations([NotNull] this Claim claim, ImmutableArray destinations) { if (claim == null) { throw new ArgumentNullException(nameof(claim)); } - if (destinations == null || !destinations.Any()) + if (destinations.IsDefaultOrEmpty) { claim.Properties.Remove(Properties.Destinations); @@ -596,10 +603,16 @@ namespace OpenIddict.Abstractions /// /// The instance. /// The destinations. - public static Claim SetDestinations([NotNull] this Claim claim, params string[] destinations) - // Note: guarding the destinations parameter against null values - // is not necessary as AsEnumerable() doesn't throw on null values. - => claim.SetDestinations(destinations.AsEnumerable()); + public static Claim SetDestinations([NotNull] this Claim claim, [CanBeNull] IEnumerable destinations) + => claim.SetDestinations(destinations?.ToImmutableArray() ?? ImmutableArray.Create()); + + /// + /// Adds specific destinations to a claim. + /// + /// The instance. + /// The destinations. + public static Claim SetDestinations([NotNull] this Claim claim, [CanBeNull] params string[] destinations) + => claim.SetDestinations(destinations?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Clones an identity by filtering its claims and the claims of its actor, recursively. @@ -714,7 +727,7 @@ namespace OpenIddict.Abstractions public static ClaimsIdentity AddClaim( [NotNull] this ClaimsIdentity identity, [NotNull] string type, [NotNull] string value, - [NotNull] IEnumerable destinations) + [NotNull] ImmutableArray destinations) { if (identity == null) { @@ -751,9 +764,7 @@ namespace OpenIddict.Abstractions [NotNull] this ClaimsIdentity identity, [NotNull] string type, [NotNull] string value, [NotNull] params string[] destinations) - // Note: guarding the destinations parameter against null values - // is not necessary as AsEnumerable() doesn't throw on null values. - => identity.AddClaim(type, value, destinations.AsEnumerable()); + => identity.AddClaim(type, value, destinations?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Gets the claim value corresponding to the given type. @@ -803,7 +814,7 @@ namespace OpenIddict.Abstractions /// The identity. /// The type associated with the claims. /// The claim values. - public static ImmutableHashSet GetClaims([NotNull] this ClaimsIdentity identity, [NotNull] string type) + public static ImmutableArray GetClaims([NotNull] this ClaimsIdentity identity, [NotNull] string type) { if (identity == null) { @@ -815,7 +826,7 @@ namespace OpenIddict.Abstractions throw new ArgumentException("The claim type cannot be null or empty.", nameof(type)); } - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, identity.FindAll(type).Select(claim => claim.Value)); + return identity.FindAll(type).Select(claim => claim.Value).Distinct(StringComparer.Ordinal).ToImmutableArray(); } /// @@ -824,7 +835,7 @@ namespace OpenIddict.Abstractions /// The principal. /// The type associated with the claims. /// The claim values. - public static ImmutableHashSet GetClaims([NotNull] this ClaimsPrincipal principal, [NotNull] string type) + public static ImmutableArray GetClaims([NotNull] this ClaimsPrincipal principal, [NotNull] string type) { if (principal == null) { @@ -836,7 +847,7 @@ namespace OpenIddict.Abstractions throw new ArgumentException("The claim type cannot be null or empty.", nameof(type)); } - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, principal.FindAll(type).Select(claim => claim.Value)); + return principal.FindAll(type).Select(claim => claim.Value).Distinct(StringComparer.Ordinal).ToImmutableArray(); } /// @@ -964,7 +975,7 @@ namespace OpenIddict.Abstractions /// The claim values. /// The claims identity. public static ClaimsIdentity SetClaims([NotNull] this ClaimsIdentity identity, - [NotNull] string type, [NotNull] IEnumerable values) + [NotNull] string type, [NotNull] ImmutableArray values) { if (identity == null) { @@ -978,7 +989,7 @@ namespace OpenIddict.Abstractions identity.RemoveClaims(type); - foreach (var value in values) + foreach (var value in values.Distinct(StringComparer.Ordinal)) { identity.AddClaim(type, value); } @@ -994,7 +1005,7 @@ namespace OpenIddict.Abstractions /// The claim values. /// The claims identity. public static ClaimsPrincipal SetClaims([NotNull] this ClaimsPrincipal principal, - [NotNull] string type, [NotNull] IEnumerable values) + [NotNull] string type, [NotNull] ImmutableArray values) { if (principal == null) { @@ -1008,7 +1019,7 @@ namespace OpenIddict.Abstractions principal.RemoveClaims(type); - foreach (var value in values) + foreach (var value in values.Distinct(StringComparer.Ordinal)) { ((ClaimsIdentity) principal.Identity).AddClaim(type, value); } @@ -1073,60 +1084,32 @@ namespace OpenIddict.Abstractions /// /// The claims principal. /// The audiences list or an empty set if the claims cannot be found. - public static ImmutableHashSet GetAudiences([NotNull] this ClaimsPrincipal principal) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, principal.GetClaims(Claims.Audience)); - } + public static ImmutableArray GetAudiences([NotNull] this ClaimsPrincipal principal) + => principal.GetClaims(Claims.Audience); /// /// Gets the presenters list stored in the claims principal. /// /// The claims principal. /// The presenters list or an empty set if the claims cannot be found. - public static ImmutableHashSet GetPresenters([NotNull] this ClaimsPrincipal principal) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, principal.GetClaims(Claims.Private.Presenters)); - } + public static ImmutableArray GetPresenters([NotNull] this ClaimsPrincipal principal) + => principal.GetClaims(Claims.Private.Presenters); /// /// Gets the resources list stored in the claims principal. /// /// The claims principal. /// The resources list or an empty set if the claims cannot be found. - public static ImmutableHashSet GetResources([NotNull] this ClaimsPrincipal principal) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, principal.GetClaims(Claims.Private.Resources)); - } + public static ImmutableArray GetResources([NotNull] this ClaimsPrincipal principal) + => principal.GetClaims(Claims.Private.Resources); /// /// Gets the scopes list stored in the claims principal. /// /// The claims principal. /// The scopes list or an empty set if the claim cannot be found. - public static ImmutableHashSet GetScopes([NotNull] this ClaimsPrincipal principal) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return ImmutableHashSet.CreateRange(StringComparer.Ordinal, principal.GetClaims(Claims.Private.Scopes)); - } + public static ImmutableArray GetScopes([NotNull] this ClaimsPrincipal principal) + => principal.GetClaims(Claims.Private.Scopes); /// /// Gets the access token lifetime associated with the claims principal. @@ -1296,14 +1279,7 @@ namespace OpenIddict.Abstractions /// The claims principal. /// The unique identifier or null if the claim cannot be found. public static string GetInternalAuthorizationId([NotNull] this ClaimsPrincipal principal) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.GetClaim(Claims.Private.AuthorizationId); - } + => principal.GetClaim(Claims.Private.AuthorizationId); /// /// Gets the internal token identifier associated with the claims principal. @@ -1311,14 +1287,7 @@ namespace OpenIddict.Abstractions /// The claims principal. /// The unique identifier or null if the claim cannot be found. public static string GetInternalTokenId([NotNull] this ClaimsPrincipal principal) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.GetClaim(Claims.Private.TokenId); - } + => principal.GetClaim(Claims.Private.TokenId); /// /// Gets the token usage associated with the claims principal. @@ -1326,14 +1295,7 @@ namespace OpenIddict.Abstractions /// The claims principal. /// The token usage or null if the claim cannot be found. public static string GetTokenUsage([NotNull] this ClaimsPrincipal principal) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.GetClaim(Claims.Private.TokenUsage); - } + => principal.GetClaim(Claims.Private.TokenUsage); /// /// Gets a boolean value indicating whether the @@ -1432,7 +1394,15 @@ namespace OpenIddict.Abstractions throw new ArgumentException("The audience cannot be null or empty.", nameof(audience)); } - return principal.GetAudiences().Contains(audience); + foreach (var claim in principal.FindAll(Claims.Audience)) + { + if (string.Equals(claim.Value, audience, StringComparison.Ordinal)) + { + return true; + } + } + + return false; } /// @@ -1468,7 +1438,15 @@ namespace OpenIddict.Abstractions throw new ArgumentException("The presenter cannot be null or empty.", nameof(presenter)); } - return principal.GetPresenters().Contains(presenter); + foreach (var claim in principal.FindAll(Claims.Private.Presenters)) + { + if (string.Equals(claim.Value, presenter, StringComparison.Ordinal)) + { + return true; + } + } + + return false; } /// @@ -1504,7 +1482,15 @@ namespace OpenIddict.Abstractions throw new ArgumentException("The resource cannot be null or empty.", nameof(resource)); } - return principal.GetResources().Contains(resource); + foreach (var claim in principal.FindAll(Claims.Private.Resources)) + { + if (string.Equals(claim.Value, resource, StringComparison.Ordinal)) + { + return true; + } + } + + return false; } /// @@ -1540,7 +1526,15 @@ namespace OpenIddict.Abstractions throw new ArgumentException("The scope cannot be null or empty.", nameof(scope)); } - return principal.GetScopes().Contains(scope); + foreach (var claim in principal.FindAll(Claims.Private.Scopes)) + { + if (string.Equals(claim.Value, scope, StringComparison.Ordinal)) + { + return true; + } + } + + return false; } /// @@ -1601,16 +1595,19 @@ namespace OpenIddict.Abstractions /// The audiences to store. /// The claims principal. public static ClaimsPrincipal SetAudiences( - [NotNull] this ClaimsPrincipal principal, - [CanBeNull] IEnumerable audiences) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } + [NotNull] this ClaimsPrincipal principal, [CanBeNull] ImmutableArray audiences) + => principal.SetClaims(Claims.Audience, audiences); - return principal.SetClaims(Claims.Audience, audiences.Distinct(StringComparer.Ordinal)); - } + /// + /// Sets the audiences list in the claims principal. + /// Note: this method automatically excludes duplicate audiences. + /// + /// The claims principal. + /// The audiences to store. + /// The claims principal. + public static ClaimsPrincipal SetAudiences( + [NotNull] this ClaimsPrincipal principal, [CanBeNull] IEnumerable audiences) + => principal.SetAudiences(audiences?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the audiences list in the claims principal. @@ -1621,9 +1618,7 @@ namespace OpenIddict.Abstractions /// The claims principal. public static ClaimsPrincipal SetAudiences( [NotNull] this ClaimsPrincipal principal, [CanBeNull] params string[] audiences) - // Note: guarding the audiences parameter against null values - // is not necessary as AsEnumerable() doesn't throw on null values. - => principal.SetAudiences(audiences.AsEnumerable()); + => principal.SetAudiences(audiences?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the presenters list in the claims principal. @@ -1633,16 +1628,19 @@ namespace OpenIddict.Abstractions /// The presenters to store. /// The claims principal. public static ClaimsPrincipal SetPresenters( - [NotNull] this ClaimsPrincipal principal, - [CanBeNull] IEnumerable presenters) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } + [NotNull] this ClaimsPrincipal principal, [CanBeNull] ImmutableArray presenters) + => principal.SetClaims(Claims.Private.Presenters, presenters); - return principal.SetClaims(Claims.Private.Presenters, presenters.Distinct(StringComparer.Ordinal)); - } + /// + /// Sets the presenters list in the claims principal. + /// Note: this method automatically excludes duplicate presenters. + /// + /// The claims principal. + /// The presenters to store. + /// The claims principal. + public static ClaimsPrincipal SetPresenters( + [NotNull] this ClaimsPrincipal principal, [CanBeNull] IEnumerable presenters) + => principal.SetPresenters(presenters?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the presenters list in the claims principal. @@ -1653,9 +1651,7 @@ namespace OpenIddict.Abstractions /// The claims principal. public static ClaimsPrincipal SetPresenters( [NotNull] this ClaimsPrincipal principal, [CanBeNull] params string[] presenters) - // Note: guarding the presenters parameter against null values - // is not necessary as AsEnumerable() doesn't throw on null values. - => principal.SetPresenters(presenters.AsEnumerable()); + => principal.SetPresenters(presenters?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the resources list in the claims principal. @@ -1665,16 +1661,19 @@ namespace OpenIddict.Abstractions /// The resources to store. /// The claims principal. public static ClaimsPrincipal SetResources( - [NotNull] this ClaimsPrincipal principal, - [CanBeNull] IEnumerable resources) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } + [NotNull] this ClaimsPrincipal principal, [CanBeNull] ImmutableArray resources) + => principal.SetClaims(Claims.Private.Resources, resources); - return principal.SetClaims(Claims.Private.Resources, resources.Distinct(StringComparer.Ordinal)); - } + /// + /// Sets the resources list in the claims principal. + /// Note: this method automatically excludes duplicate resources. + /// + /// The claims principal. + /// The resources to store. + /// The claims principal. + public static ClaimsPrincipal SetResources( + [NotNull] this ClaimsPrincipal principal, [CanBeNull] IEnumerable resources) + => principal.SetResources(resources?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the resources list in the claims principal. @@ -1685,9 +1684,7 @@ namespace OpenIddict.Abstractions /// The claims principal. public static ClaimsPrincipal SetResources( [NotNull] this ClaimsPrincipal principal, [CanBeNull] params string[] resources) - // Note: guarding the resources parameter against null values - // is not necessary as AsEnumerable() doesn't throw on null values. - => principal.SetResources(resources.AsEnumerable()); + => principal.SetResources(resources?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the scopes list in the claims principal. @@ -1697,15 +1694,19 @@ namespace OpenIddict.Abstractions /// The scopes to store. /// The claims principal. public static ClaimsPrincipal SetScopes( - [NotNull] this ClaimsPrincipal principal, [CanBeNull] IEnumerable scopes) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } + [NotNull] this ClaimsPrincipal principal, [CanBeNull] ImmutableArray scopes) + => principal.SetClaims(Claims.Private.Scopes, scopes); - return principal.SetClaims(Claims.Private.Scopes, scopes.Distinct(StringComparer.Ordinal)); - } + /// + /// Sets the scopes list in the claims principal. + /// Note: this method automatically excludes duplicate scopes. + /// + /// The claims principal. + /// The scopes to store. + /// The claims principal. + public static ClaimsPrincipal SetScopes( + [NotNull] this ClaimsPrincipal principal, [CanBeNull] IEnumerable scopes) + => principal.SetScopes(scopes?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the scopes list in the claims principal. @@ -1716,9 +1717,7 @@ namespace OpenIddict.Abstractions /// The claims principal. public static ClaimsPrincipal SetScopes( [NotNull] this ClaimsPrincipal principal, [CanBeNull] params string[] scopes) - // Note: guarding the scopes parameter against null values - // is not necessary as AsEnumerable() doesn't throw on null values. - => principal.SetScopes(scopes.AsEnumerable()); + => principal.SetScopes(scopes?.ToImmutableArray() ?? ImmutableArray.Create()); /// /// Sets the access token lifetime associated with the claims principal. @@ -1727,14 +1726,7 @@ namespace OpenIddict.Abstractions /// The access token lifetime to store. /// The claims principal. public static ClaimsPrincipal SetAccessTokenLifetime([NotNull] this ClaimsPrincipal principal, TimeSpan? lifetime) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.AccessTokenLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); - } + => principal.SetClaim(Claims.Private.AccessTokenLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); /// /// Sets the authorization code lifetime associated with the claims principal. @@ -1743,14 +1735,7 @@ namespace OpenIddict.Abstractions /// The authorization code lifetime to store. /// The claims principal. public static ClaimsPrincipal SetAuthorizationCodeLifetime([NotNull] this ClaimsPrincipal principal, TimeSpan? lifetime) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.AuthorizationCodeLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); - } + => principal.SetClaim(Claims.Private.AuthorizationCodeLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); /// /// Sets the device code lifetime associated with the claims principal. @@ -1759,14 +1744,7 @@ namespace OpenIddict.Abstractions /// The device code lifetime to store. /// The claims principal. public static ClaimsPrincipal SetDeviceCodeLifetime([NotNull] this ClaimsPrincipal principal, TimeSpan? lifetime) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.DeviceCodeLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); - } + => principal.SetClaim(Claims.Private.DeviceCodeLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); /// /// Sets the identity token lifetime associated with the claims principal. @@ -1775,14 +1753,7 @@ namespace OpenIddict.Abstractions /// The identity token lifetime to store. /// The claims principal. public static ClaimsPrincipal SetIdentityTokenLifetime([NotNull] this ClaimsPrincipal principal, TimeSpan? lifetime) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.IdentityTokenLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); - } + => principal.SetClaim(Claims.Private.IdentityTokenLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); /// /// Sets the refresh token lifetime associated with the claims principal. @@ -1791,14 +1762,7 @@ namespace OpenIddict.Abstractions /// The refresh token lifetime to store. /// The claims principal. public static ClaimsPrincipal SetRefreshTokenLifetime([NotNull] this ClaimsPrincipal principal, TimeSpan? lifetime) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.RefreshTokenLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); - } + => principal.SetClaim(Claims.Private.RefreshTokenLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); /// /// Sets the user code lifetime associated with the claims principal. @@ -1807,14 +1771,7 @@ namespace OpenIddict.Abstractions /// The user code lifetime to store. /// The claims principal. public static ClaimsPrincipal SetUserCodeLifetime([NotNull] this ClaimsPrincipal principal, TimeSpan? lifetime) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.UserCodeLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); - } + => principal.SetClaim(Claims.Private.UserCodeLifetime, lifetime?.TotalSeconds.ToString(CultureInfo.InvariantCulture)); /// /// Sets the internal authorization identifier associated with the claims principal. @@ -1823,14 +1780,7 @@ namespace OpenIddict.Abstractions /// The unique identifier to store. /// The claims principal. public static ClaimsPrincipal SetInternalAuthorizationId([NotNull] this ClaimsPrincipal principal, string identifier) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.AuthorizationId, identifier); - } + => principal.SetClaim(Claims.Private.AuthorizationId, identifier); /// /// Sets the internal token identifier associated with the claims principal. @@ -1839,14 +1789,7 @@ namespace OpenIddict.Abstractions /// The unique identifier to store. /// The claims principal. public static ClaimsPrincipal SetInternalTokenId([NotNull] this ClaimsPrincipal principal, string identifier) - { - if (principal == null) - { - throw new ArgumentNullException(nameof(principal)); - } - - return principal.SetClaim(Claims.Private.TokenId, identifier); - } + => principal.SetClaim(Claims.Private.TokenId, identifier); private static IEnumerable GetValues(string source, char[] separators) { diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs index eb6c2c16..5db97b2d 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs @@ -696,9 +696,8 @@ namespace OpenIddict.Core throw new ArgumentNullException(nameof(authorization)); } - return (await Store.GetScopesAsync(authorization, cancellationToken)) - .ToImmutableHashSet(StringComparer.Ordinal) - .IsSupersetOf(scopes); + return new HashSet(await Store.GetScopesAsync( + authorization, cancellationToken), StringComparer.Ordinal).IsSupersetOf(scopes); } /// diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs index bca01712..837f35e0 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionFormatter.cs @@ -173,8 +173,9 @@ namespace OpenIddict.Server.DataProtection static string GetProperty(IReadOnlyDictionary properties, string name) => properties.TryGetValue(name, out var value) ? value : null; - static IEnumerable GetArrayProperty(IReadOnlyDictionary properties, string name) - => properties.TryGetValue(name, out var value) ? JArray.Parse(value).Values() : Enumerable.Empty(); + static ImmutableArray GetArrayProperty(IReadOnlyDictionary properties, string name) + => properties.TryGetValue(name, out var value) ? + JArray.Parse(value).Values().ToImmutableArray() : ImmutableArray.Create(); static DateTimeOffset? GetDateProperty(IReadOnlyDictionary properties, string name) => properties.TryGetValue(name, out var value) ? (DateTimeOffset?) diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs index f8a7373e..6de87249 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs @@ -5,9 +5,9 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Security.Claims; using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; @@ -16,7 +16,6 @@ using OpenIddict.Abstractions; using static OpenIddict.Abstractions.OpenIddictConstants; using static OpenIddict.Server.OpenIddictServerEvents; using static OpenIddict.Server.OpenIddictServerHandlerFilters; -using Properties = OpenIddict.Server.OpenIddictServerConstants.Properties; namespace OpenIddict.Server { @@ -637,7 +636,7 @@ namespace OpenIddict.Server } // Reject requests that specify an unsupported response_type. - var types = context.Request.GetResponseTypes(); + var types = new HashSet(context.Request.GetResponseTypes(), StringComparer.Ordinal); if (!context.Options.ResponseTypes.Any(type => types.SetEquals(type.Split(Separators.Space, StringSplitOptions.RemoveEmptyEntries)))) { @@ -1245,12 +1244,14 @@ namespace OpenIddict.Server } // If all the specified scopes are registered in the options, avoid making a database lookup. - var scopes = context.Request.GetScopes().Except(context.Options.Scopes); + var scopes = new HashSet(context.Request.GetScopes(), StringComparer.Ordinal); + scopes.ExceptWith(context.Options.Scopes); + if (scopes.Count != 0) { await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { - scopes = scopes.Remove(await _scopeManager.GetNameAsync(scope)); + scopes.Remove(await _scopeManager.GetNameAsync(scope)); } } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs index 20fedf5a..c8e043e5 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs @@ -5,6 +5,7 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Security.Claims; using System.Text; @@ -454,12 +455,14 @@ namespace OpenIddict.Server } // If all the specified scopes are registered in the options, avoid making a database lookup. - var scopes = context.Request.GetScopes().Except(context.Options.Scopes); + var scopes = new HashSet(context.Request.GetScopes(), StringComparer.Ordinal); + scopes.ExceptWith(context.Options.Scopes); + if (scopes.Count != 0) { await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { - scopes = scopes.Remove(await _scopeManager.GetNameAsync(scope)); + scopes.Remove(await _scopeManager.GetNameAsync(scope)); } } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs index c047ba1e..b605d24c 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs @@ -5,7 +5,9 @@ */ using System; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; @@ -760,12 +762,14 @@ namespace OpenIddict.Server } // If all the specified scopes are registered in the options, avoid making a database lookup. - var scopes = context.Request.GetScopes().Except(context.Options.Scopes); + var scopes = new HashSet(context.Request.GetScopes(), StringComparer.Ordinal); + scopes.ExceptWith(context.Options.Scopes); + if (scopes.Count != 0) { await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { - scopes = scopes.Remove(await _scopeManager.GetNameAsync(scope)); + scopes.Remove(await _scopeManager.GetNameAsync(scope)); } } @@ -1422,7 +1426,7 @@ namespace OpenIddict.Server } var presenters = context.Principal.GetPresenters(); - if (presenters.Count == 0) + if (presenters.IsDefaultOrEmpty) { // Note: presenters may be empty during a grant_type=refresh_token request if the refresh token // was issued to a public client but cannot be null for an authorization or device code grant request. @@ -1725,7 +1729,7 @@ namespace OpenIddict.Server // When an explicit scope parameter has been included in the token request // but was missing from the initial request, the request MUST be rejected. // See http://tools.ietf.org/html/rfc6749#section-6 for more information. - var scopes = context.Principal.GetScopes(); + var scopes = new HashSet(context.Principal.GetScopes(), StringComparer.Ordinal); if (scopes.Count == 0) { context.Logger.LogError("The token request was rejected because the 'scope' parameter was not allowed."); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index 0f456114..95f41642 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -3855,10 +3855,11 @@ namespace OpenIddict.Server // If the granted access token scopes differ from the requested scopes, return the granted scopes // list as a parameter to inform the client application of the fact the scopes set will be reduced. + var scopes = new HashSet(context.AccessTokenPrincipal.GetScopes(), StringComparer.Ordinal); if ((context.EndpointType == OpenIddictServerEndpointType.Token && context.Request.IsAuthorizationCodeGrantType()) || - !context.AccessTokenPrincipal.GetScopes().SetEquals(context.Request.GetScopes())) + !scopes.SetEquals(context.Request.GetScopes())) { - context.Response.Scope = string.Join(" ", context.AccessTokenPrincipal.GetScopes()); + context.Response.Scope = string.Join(" ", scopes); } return default; diff --git a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs index bf9595f5..db53b44f 100644 --- a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs +++ b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionFormatter.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.IO; -using System.Linq; using System.Security.Claims; using JetBrains.Annotations; using Newtonsoft.Json.Linq; @@ -172,8 +171,9 @@ namespace OpenIddict.Validation.DataProtection static string GetProperty(IReadOnlyDictionary properties, string name) => properties.TryGetValue(name, out var value) ? value : null; - static IEnumerable GetArrayProperty(IReadOnlyDictionary properties, string name) - => properties.TryGetValue(name, out var value) ? JArray.Parse(value).Values() : Enumerable.Empty(); + static ImmutableArray GetArrayProperty(IReadOnlyDictionary properties, string name) + => properties.TryGetValue(name, out var value) ? + JArray.Parse(value).Values().ToImmutableArray() : ImmutableArray.Create(); static DateTimeOffset? GetDateProperty(IReadOnlyDictionary properties, string name) => properties.TryGetValue(name, out var value) ? (DateTimeOffset?) diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs index a6f851bb..f145ed1e 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs @@ -409,7 +409,8 @@ namespace OpenIddict.Validation } // If the access token doesn't have any audience attached, return an error. - if (!context.Principal.HasAudience()) + var audiences = context.Principal.GetAudiences(); + if (audiences.IsDefaultOrEmpty) { context.Logger.LogError("The request was rejected because the access token had no audience attached."); @@ -421,7 +422,7 @@ namespace OpenIddict.Validation } // If the access token doesn't include any registered audience, return an error. - if (context.Principal.GetAudiences().Intersect(context.Options.Audiences).IsEmpty) + if (!audiences.Intersect(context.Options.Audiences, StringComparer.Ordinal).Any()) { context.Logger.LogError("The request was rejected because the access token had no valid audience."); diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs index 11543477..eaf52914 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs @@ -42,15 +42,8 @@ namespace OpenIddict.Abstractions.Tests.Primitives AcrValues = value }; - // Act - var actualValues = request.GetAcrValues(); - - // Assert - Assert.Equal(values.Length, actualValues.Count); - foreach (var val in actualValues) - { - Assert.Contains(val, values); - } + // Act and assert + Assert.Equal(values, request.GetAcrValues()); } [Fact] @@ -77,7 +70,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives [InlineData(" code id_token", new[] { "code", "id_token" })] [InlineData("code code id_token", new[] { "code", "id_token" })] [InlineData("code CODE id_token", new[] { "code", "CODE", "id_token" })] - public void GetResponseTypes_ReturnsExpectedResponseTypes(string value, string[] responseTypes) + public void GetResponseTypes_ReturnsExpectedResponseTypes(string value, string[] values) { // Arrange var request = new OpenIddictRequest @@ -85,15 +78,8 @@ namespace OpenIddict.Abstractions.Tests.Primitives ResponseType = value }; - // Act - var actualResponseTypes = request.GetResponseTypes(); - - // Assert - Assert.Equal(responseTypes.Length, actualResponseTypes.Count); - foreach (var rt in actualResponseTypes) - { - Assert.Contains(rt, responseTypes); - } + // Act and assert + Assert.Equal(values, request.GetResponseTypes()); } [Fact] @@ -127,15 +113,8 @@ namespace OpenIddict.Abstractions.Tests.Primitives Scope = scope }; - // Act - var actualScopes = request.GetScopes(); - - // Assert - Assert.Equal(scopes.Length, actualScopes.Count); - foreach (var scp in actualScopes) - { - Assert.Contains(scp, scopes); - } + // Act and assert + Assert.Equal(scopes, request.GetScopes()); } [Fact]