diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs
index 12b4fdfb..6ad7131b 100644
--- a/samples/Mvc.Server/Startup.cs
+++ b/samples/Mvc.Server/Startup.cs
@@ -90,9 +90,10 @@ namespace Mvc.Server
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
- // Mark the "email" and "profile" scopes as supported scopes.
+ // Mark the "email", "profile" and "roles" scopes as supported scopes.
options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
- OpenIdConnectConstants.Scopes.Profile);
+ OpenIdConnectConstants.Scopes.Profile,
+ OpenIddictConstants.Scopes.Roles);
// Make the "client_id" parameter mandatory when sending a token request.
options.RequireClientIdentification();
diff --git a/src/OpenIddict.Core/Descriptors/OpenIddictApplicationDescriptor.cs b/src/OpenIddict.Core/Descriptors/OpenIddictApplicationDescriptor.cs
index 8f1524cc..ad68de3d 100644
--- a/src/OpenIddict.Core/Descriptors/OpenIddictApplicationDescriptor.cs
+++ b/src/OpenIddict.Core/Descriptors/OpenIddictApplicationDescriptor.cs
@@ -21,6 +21,12 @@ namespace OpenIddict.Core
///
public string ClientSecret { get; set; }
+ ///
+ /// Gets or sets the consent type
+ /// associated with the application.
+ ///
+ public virtual string ConsentType { get; set; }
+
///
/// Gets or sets the display name
/// associated with the application.
diff --git a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
index 84c3ed7d..e60ab678 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
@@ -370,6 +370,31 @@ namespace OpenIddict.Core
return type;
}
+ ///
+ /// Retrieves the consent type associated with an application.
+ ///
+ /// The application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the consent type of the application (by default, "explicit").
+ ///
+ public virtual async Task GetConsentTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken = default)
+ {
+ if (application == null)
+ {
+ throw new ArgumentNullException(nameof(application));
+ }
+
+ var type = await Store.GetConsentTypeAsync(application, cancellationToken);
+ if (string.IsNullOrEmpty(type))
+ {
+ return OpenIddictConstants.ConsentTypes.Explicit;
+ }
+
+ return type;
+ }
+
///
/// Retrieves the display name associated with an application.
///
@@ -799,6 +824,7 @@ namespace OpenIddict.Core
{
ClientId = await Store.GetClientIdAsync(application, cancellationToken),
ClientSecret = secret,
+ ConsentType = await Store.GetConsentTypeAsync(application, cancellationToken),
DisplayName = await Store.GetDisplayNameAsync(application, cancellationToken),
Type = await Store.GetClientTypeAsync(application, cancellationToken)
};
@@ -1161,6 +1187,7 @@ namespace OpenIddict.Core
await Store.SetClientIdAsync(application, descriptor.ClientId, cancellationToken);
await Store.SetClientSecretAsync(application, descriptor.ClientSecret, cancellationToken);
await Store.SetClientTypeAsync(application, descriptor.Type, cancellationToken);
+ await Store.SetConsentTypeAsync(application, descriptor.ConsentType, cancellationToken);
await Store.SetDisplayNameAsync(application, descriptor.DisplayName, cancellationToken);
await Store.SetPermissionsAsync(application, ImmutableArray.CreateRange(descriptor.Permissions), cancellationToken);
await Store.SetPostLogoutRedirectUrisAsync(application, ImmutableArray.CreateRange(
diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index 71a2bf68..7994f4c3 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -5,9 +5,11 @@
*/
using System;
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -88,6 +90,12 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(authorization));
}
+ // If no status was explicitly specified, assume that the authorization is valid.
+ if (string.IsNullOrEmpty(await Store.GetStatusAsync(authorization, cancellationToken)))
+ {
+ await Store.SetStatusAsync(authorization, OpenIddictConstants.Statuses.Valid, cancellationToken);
+ }
+
// If no type was explicitly specified, assume that the authorization is a permanent authorization.
if (string.IsNullOrEmpty(await Store.GetTypeAsync(authorization, cancellationToken)))
{
@@ -131,6 +139,58 @@ namespace OpenIddict.Core
return authorization;
}
+ ///
+ /// Creates a new permanent authorization based on the specified parameters.
+ ///
+ /// The principal associated with the authorization.
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The minimal scopes associated with the authorization.
+ /// The authentication properties associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation, whose result returns the authorization.
+ ///
+ public virtual Task CreateAsync(
+ [NotNull] ClaimsPrincipal principal, [NotNull] string subject,
+ [NotNull] string client, ImmutableArray scopes,
+ [CanBeNull] ImmutableDictionary properties, CancellationToken cancellationToken = default)
+ {
+ if (principal == null)
+ {
+ throw new ArgumentNullException(nameof(principal));
+ }
+
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException("The client identifier cannot be null or empty.", nameof(client));
+ }
+
+ var descriptor = new OpenIddictAuthorizationDescriptor
+ {
+ ApplicationId = client,
+ Principal = principal,
+ Subject = subject
+ };
+
+ descriptor.Scopes.UnionWith(scopes);
+
+ if (properties != null)
+ {
+ foreach (var property in properties)
+ {
+ descriptor.Properties.Add(property);
+ }
+ }
+
+ return CreateAsync(descriptor, cancellationToken);
+ }
+
///
/// Removes an existing authorization.
///
@@ -176,6 +236,57 @@ namespace OpenIddict.Core
return Store.FindAsync(subject, client, cancellationToken);
}
+ ///
+ /// Retrieves the authorizations matching the specified parameters.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The status associated with the authorization.
+ /// The type associated with the authorization.
+ /// The minimal scopes associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorizations corresponding to the subject/client.
+ ///
+ public virtual async Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, [NotNull] string type,
+ ImmutableArray scopes, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException("The client identifier cannot be null or empty.", nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException("The status cannot be null or empty.", nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException("The type cannot be null or empty.", nameof(client));
+ }
+
+ var authorizations = ImmutableArray.CreateBuilder();
+
+ foreach (var authorization in await Store.FindAsync(subject, client, status, type, cancellationToken))
+ {
+ if (await HasScopesAsync(authorization, scopes, cancellationToken))
+ {
+ authorizations.Add(authorization);
+ }
+ }
+
+ return authorizations.ToImmutable();
+ }
+
///
/// Retrieves an authorization using its unique identifier.
///
@@ -195,6 +306,26 @@ namespace OpenIddict.Core
return Store.FindByIdAsync(identifier, cancellationToken);
}
+ ///
+ /// Retrieves all the authorizations corresponding to the specified subject.
+ ///
+ /// The subject associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorizations corresponding to the specified subject.
+ ///
+ public virtual Task> FindBySubjectAsync(
+ [NotNull] string subject, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
+ }
+
+ return Store.FindBySubjectAsync(subject, cancellationToken);
+ }
+
///
/// Retrieves the optional application identifier associated with an authorization.
///
diff --git a/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs b/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
index bd3659e2..b0eecfb4 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
@@ -501,11 +501,29 @@ namespace OpenIddict.Core
var results = ImmutableArray.CreateBuilder();
- if (string.IsNullOrEmpty(await Store.GetNameAsync(scope, cancellationToken)))
+ var name = await Store.GetNameAsync(scope, cancellationToken);
+ if (string.IsNullOrEmpty(name))
{
results.Add(new ValidationResult("The scope name cannot be null or empty."));
}
+ else if (name.Contains(OpenIddictConstants.Separators.Space))
+ {
+ results.Add(new ValidationResult("The scope name cannot contain spaces."));
+ }
+
+ else
+ {
+ // Ensure the name is not already used for a different name.
+ var other = await Store.FindByNameAsync(name, cancellationToken);
+ if (other != null && !string.Equals(
+ await Store.GetIdAsync(other, cancellationToken),
+ await Store.GetIdAsync(scope, cancellationToken), StringComparison.Ordinal))
+ {
+ results.Add(new ValidationResult("A scope with the same name already exists."));
+ }
+ }
+
return results.ToImmutable();
}
diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
index f0a52317..96094279 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
@@ -90,6 +90,12 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(token));
}
+ // If no status was explicitly specified, assume that the token is valid.
+ if (string.IsNullOrEmpty(await Store.GetStatusAsync(token, cancellationToken)))
+ {
+ await Store.SetStatusAsync(token, OpenIddictConstants.Statuses.Valid, cancellationToken);
+ }
+
var results = await ValidateAsync(token, cancellationToken);
if (results.Any(result => result != ValidationResult.Success))
{
diff --git a/src/OpenIddict.Core/OpenIddictConstants.cs b/src/OpenIddict.Core/OpenIddictConstants.cs
index 5ff54b19..c4185636 100644
--- a/src/OpenIddict.Core/OpenIddictConstants.cs
+++ b/src/OpenIddict.Core/OpenIddictConstants.cs
@@ -26,6 +26,13 @@ namespace OpenIddict.Core
public const string Public = "public";
}
+ public static class ConsentTypes
+ {
+ public const string Explicit = "explicit";
+ public const string External = "external";
+ public const string Implicit = "implicit";
+ }
+
public static class Environment
{
public const string AuthorizationRequest = "openiddict-authorization-request:";
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
index 7d44a177..11f11698 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
@@ -157,6 +157,17 @@ namespace OpenIddict.Core
///
Task GetClientTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken);
+ ///
+ /// Retrieves the consent type associated with an application.
+ ///
+ /// The application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the consent type of the application (by default, "explicit").
+ ///
+ Task GetConsentTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken);
+
///
/// Retrieves the display name associated with an application.
///
@@ -296,6 +307,17 @@ namespace OpenIddict.Core
///
Task SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken);
+ ///
+ /// Sets the consent type associated with an application.
+ ///
+ /// The application.
+ /// The consent type associated with the application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ Task SetConsentTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken);
+
///
/// Sets the display name associated with an application.
///
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
index 7c97cac9..43f0552a 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
@@ -75,6 +75,22 @@ namespace OpenIddict.Core
///
Task> FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken);
+ ///
+ /// Retrieves the authorizations matching the specified parameters.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The status associated with the authorization.
+ /// The type associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorizations corresponding to the subject/client.
+ ///
+ Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken);
+
///
/// Retrieves an authorization using its unique identifier.
///
@@ -86,6 +102,17 @@ namespace OpenIddict.Core
///
Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken);
+ ///
+ /// Retrieves all the authorizations corresponding to the specified subject.
+ ///
+ /// The subject associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorizations corresponding to the specified subject.
+ ///
+ Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken);
+
///
/// Retrieves the optional application identifier associated with an authorization.
///
diff --git a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs
index d183a01e..dabc46e4 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs
@@ -307,6 +307,25 @@ namespace OpenIddict.Core
return Task.FromResult(application.Type);
}
+ ///
+ /// Retrieves the consent type associated with an application.
+ ///
+ /// The application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the consent type of the application (by default, "explicit").
+ ///
+ public virtual Task GetConsentTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken)
+ {
+ if (application == null)
+ {
+ throw new ArgumentNullException(nameof(application));
+ }
+
+ return Task.FromResult(application.ConsentType);
+ }
+
///
/// Retrieves the display name associated with an application.
///
@@ -603,6 +622,28 @@ namespace OpenIddict.Core
return Task.CompletedTask;
}
+ ///
+ /// Sets the consent type associated with an application.
+ ///
+ /// The application.
+ /// The consent type associated with the application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual Task SetConsentTypeAsync([NotNull] TApplication application,
+ [CanBeNull] string type, CancellationToken cancellationToken)
+ {
+ if (application == null)
+ {
+ throw new ArgumentNullException(nameof(application));
+ }
+
+ application.ConsentType = type;
+
+ return Task.CompletedTask;
+ }
+
///
/// Sets the display name associated with an application.
///
diff --git a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
index e6b468eb..e33fe5e0 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
@@ -104,7 +104,8 @@ namespace OpenIddict.Core
/// A that can be used to monitor the asynchronous operation,
/// whose result returns the authorizations corresponding to the subject/client.
///
- public virtual Task> FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken)
+ public virtual Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
@@ -118,9 +119,9 @@ namespace OpenIddict.Core
IQueryable Query(IQueryable authorizations, TKey key, string principal)
=> from authorization in authorizations
- where authorization.Application != null
- where authorization.Application.Id.Equals(key)
- where authorization.Subject == principal
+ where authorization.Application != null &&
+ authorization.Application.Id.Equals(key) &&
+ authorization.Subject == principal
select authorization;
return ListAsync(
@@ -128,6 +129,57 @@ namespace OpenIddict.Core
(key: ConvertIdentifierFromString(client), principal: subject), cancellationToken);
}
+ ///
+ /// Retrieves the authorizations matching the specified parameters.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The status associated with the authorization.
+ /// The type associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorizations corresponding to the subject/client.
+ ///
+ public virtual Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException("The client identifier cannot be null or empty.", nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException("The status cannot be null or empty.", nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException("The type cannot be null or empty.", nameof(client));
+ }
+
+ IQueryable Query(IQueryable authorizations,
+ TKey key, string principal, string state, string kind)
+ => from authorization in authorizations
+ where authorization.Application != null &&
+ authorization.Application.Id.Equals(key) &&
+ authorization.Subject == principal &&
+ authorization.Status == state &&
+ authorization.Type == kind
+ select authorization;
+
+ return ListAsync(
+ (authorizations, state) => Query(authorizations, state.key, state.principal, state.state, state.kind),
+ (key: ConvertIdentifierFromString(client), principal: subject, state: status, kind: type), cancellationToken);
+ }
+
///
/// Retrieves an authorization using its unique identifier.
///
@@ -152,6 +204,31 @@ namespace OpenIddict.Core
return GetAsync((authorizations, key) => Query(authorizations, key), ConvertIdentifierFromString(identifier), cancellationToken);
}
+ ///
+ /// Retrieves all the authorizations corresponding to the specified subject.
+ ///
+ /// The subject associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorizations corresponding to the specified subject.
+ ///
+ public virtual Task> FindBySubjectAsync(
+ [NotNull] string subject, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
+ }
+
+ IQueryable Query(IQueryable authorizations, string principal)
+ => from authorization in authorizations
+ where authorization.Subject == principal
+ select authorization;
+
+ return ListAsync((authorizations, principal) => Query(authorizations, principal), subject, cancellationToken);
+ }
+
///
/// Retrieves the optional application identifier associated with an authorization.
///
@@ -175,8 +252,8 @@ namespace OpenIddict.Core
IQueryable Query(IQueryable authorizations, TKey key)
=> from element in authorizations
- where element.Id.Equals(key)
- where element.Application != null
+ where element.Id.Equals(key) &&
+ element.Application != null
select element.Application.Id;
return ConvertIdentifierToString(await GetAsync(
diff --git a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
index 73142d9c..679e89e1 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
@@ -108,8 +108,8 @@ namespace OpenIddict.Core
IQueryable Query(IQueryable tokens, TKey key)
=> from token in tokens
- where token.Application != null
- where token.Application.Id.Equals(key)
+ where token.Application != null &&
+ token.Application.Id.Equals(key)
select token;
return ListAsync((tokens, key) => Query(tokens, key), ConvertIdentifierFromString(identifier), cancellationToken);
@@ -133,8 +133,8 @@ namespace OpenIddict.Core
IQueryable Query(IQueryable tokens, TKey key)
=> from token in tokens
- where token.Authorization != null
- where token.Authorization.Id.Equals(key)
+ where token.Authorization != null &&
+ token.Authorization.Id.Equals(key)
select token;
return ListAsync((tokens, key) => Query(tokens, key), ConvertIdentifierFromString(identifier), cancellationToken);
@@ -252,8 +252,8 @@ namespace OpenIddict.Core
IQueryable Query(IQueryable tokens, TKey key)
=> from element in tokens
- where element.Id.Equals(key)
- where element.Application != null
+ where element.Id.Equals(key) &&
+ element.Application != null
select element.Application.Id;
return ConvertIdentifierToString(await GetAsync((tokens, key) => Query(tokens, key), token.Id, cancellationToken));
@@ -282,8 +282,8 @@ namespace OpenIddict.Core
IQueryable Query(IQueryable tokens, TKey key)
=> from element in tokens
- where element.Id.Equals(key)
- where element.Authorization != null
+ where element.Id.Equals(key) &&
+ element.Authorization != null
select element.Authorization.Id;
return ConvertIdentifierToString(await GetAsync((tokens, key) => Query(tokens, key), token.Id, cancellationToken));
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
index db9e18c6..1abf8662 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
@@ -188,7 +188,8 @@ namespace OpenIddict.EntityFrameworkCore
/// A that can be used to monitor the asynchronous operation,
/// whose result returns the authorizations corresponding to the subject/client.
///
- public override async Task> FindAsync([NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken)
+ public override async Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
@@ -205,7 +206,8 @@ namespace OpenIddict.EntityFrameworkCore
// this method is overriden to use an explicit join before applying the equality check.
// See https://github.com/openiddict/openiddict-core/issues/499 for more information.
- IQueryable Query(IQueryable authorizations, IQueryable applications, TKey key, string principal)
+ IQueryable Query(IQueryable authorizations,
+ IQueryable applications, TKey key, string principal)
=> from authorization in authorizations.Include(authorization => authorization.Application)
where authorization.Subject == principal
join application in applications on authorization.Application.Id equals application.Id
@@ -216,6 +218,61 @@ namespace OpenIddict.EntityFrameworkCore
Authorizations, Applications, ConvertIdentifierFromString(client), subject).ToListAsync(cancellationToken));
}
+ ///
+ /// Retrieves the authorizations matching the specified parameters.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The status associated with the authorization.
+ /// The type associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorizations corresponding to the subject/client.
+ ///
+ public override async Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException("The client identifier cannot be null or empty.", nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException("The status cannot be null or empty.", nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException("The type cannot be null or empty.", nameof(client));
+ }
+
+ // Note: due to a bug in Entity Framework Core's query visitor, the authorizations can't be
+ // filtered using authorization.Application.Id.Equals(key). To work around this issue,
+ // this method is overriden to use an explicit join before applying the equality check.
+ // See https://github.com/openiddict/openiddict-core/issues/499 for more information.
+
+ IQueryable Query(IQueryable authorizations,
+ IQueryable applications, TKey key, string principal, string state, string kind)
+ => from authorization in authorizations.Include(authorization => authorization.Application)
+ where authorization.Subject == principal &&
+ authorization.Status == state &&
+ authorization.Type == kind
+ join application in applications on authorization.Application.Id equals application.Id
+ where application.Id.Equals(key)
+ select authorization;
+
+ return ImmutableArray.CreateRange(await Query(
+ Authorizations, Applications, ConvertIdentifierFromString(client), subject, status, type).ToListAsync(cancellationToken));
+ }
+
///
/// Retrieves an authorization using its unique identifier.
///
@@ -233,8 +290,8 @@ namespace OpenIddict.EntityFrameworkCore
}
var authorization = (from entry in Context.ChangeTracker.Entries()
- where entry.Entity != null
- where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
+ where entry.Entity != null &&
+ entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
select entry.Entity).FirstOrDefault();
if (authorization != null)
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
index 92ab42a2..6a272c5c 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
@@ -242,8 +242,8 @@ namespace OpenIddict.EntityFrameworkCore
}
var token = (from entry in Context.ChangeTracker.Entries()
- where entry.Entity != null
- where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
+ where entry.Entity != null &&
+ entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
select entry.Entity).FirstOrDefault();
if (token != null)
diff --git a/src/OpenIddict.Models/OpenIddictApplication.cs b/src/OpenIddict.Models/OpenIddictApplication.cs
index 99ae4588..0665b573 100644
--- a/src/OpenIddict.Models/OpenIddictApplication.cs
+++ b/src/OpenIddict.Models/OpenIddictApplication.cs
@@ -56,6 +56,12 @@ namespace OpenIddict.Models
///
public virtual string ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
+ ///
+ /// Gets or sets the consent type
+ /// associated with the current application.
+ ///
+ public virtual string ConsentType { get; set; }
+
///
/// Gets or sets the display name
/// associated with the current application.
diff --git a/src/OpenIddict/OpenIddictExtensions.cs b/src/OpenIddict/OpenIddictExtensions.cs
index 021800f3..33443e64 100644
--- a/src/OpenIddict/OpenIddictExtensions.cs
+++ b/src/OpenIddict/OpenIddictExtensions.cs
@@ -690,8 +690,8 @@ namespace Microsoft.Extensions.DependencyInjection
///
/// Rejects authorization and token requests that specify scopes that have not been
- /// registered in the database using
- /// or .
+ /// registered using or
+ /// .
///
/// The services builder used by OpenIddict to register new services.
public static OpenIddictBuilder EnableScopeValidation([NotNull] this OpenIddictBuilder builder)