diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index 3ddbd384..23d0dd77 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -233,6 +233,39 @@ 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 authorization status.
+ /// 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 criteria.
+ ///
+ public virtual Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, 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));
+ }
+
+ return Store.FindAsync(subject, client, status, cancellationToken);
+ }
+
///
/// Retrieves the authorizations matching the specified parameters.
///
@@ -240,16 +273,14 @@ namespace OpenIddict.Core
/// The client associated with the authorization.
/// The authorization status.
/// The authorization type.
- /// 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 criteria.
///
- public virtual async Task> FindAsync(
+ public virtual Task> FindAsync(
[NotNull] string subject, [NotNull] string client,
- [NotNull] string status, [NotNull] string type,
- ImmutableArray scopes, CancellationToken cancellationToken = default)
+ [NotNull] string status, [NotNull] string type, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(subject))
{
@@ -271,9 +302,30 @@ namespace OpenIddict.Core
throw new ArgumentException("The type cannot be null or empty.", nameof(client));
}
+ return Store.FindAsync(subject, client, status, type, cancellationToken);
+ }
+
+ ///
+ /// Retrieves the authorizations matching the specified parameters.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The authorization status.
+ /// The authorization type.
+ /// 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 criteria.
+ ///
+ public virtual async Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, [NotNull] string type,
+ ImmutableArray scopes, CancellationToken cancellationToken = default)
+ {
var authorizations = ImmutableArray.CreateBuilder();
- foreach (var authorization in await Store.FindAsync(subject, client, status, type, cancellationToken))
+ foreach (var authorization in await FindAsync(subject, client, status, type, cancellationToken))
{
if (await HasScopesAsync(authorization, scopes, cancellationToken))
{
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
index 847371ea..7c15d92c 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
@@ -73,7 +73,23 @@ namespace OpenIddict.Core
/// 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, CancellationToken cancellationToken);
+ 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 authorization status.
+ /// 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 criteria.
+ ///
+ Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, CancellationToken cancellationToken);
///
/// Retrieves the authorizations matching the specified parameters.
diff --git a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
index f7dcf14c..a85d2cce 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
@@ -129,6 +129,44 @@ 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 authorization status.
+ /// 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 criteria.
+ ///
+ public virtual Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, 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 cannot be null or empty.", nameof(client));
+ }
+
+ IQueryable Query(IQueryable authorizations, TKey key, string principal, string state)
+ => from authorization in authorizations
+ where authorization.Application != null &&
+ authorization.Application.Id.Equals(key) &&
+ authorization.Subject == principal &&
+ authorization.Status == state
+ select authorization;
+
+ return ListAsync(
+ (authorizations, state) => Query(authorizations, state.key, state.principal, state.state),
+ (key: ConvertIdentifierFromString(client), principal: subject, state: status), cancellationToken);
+ }
+
///
/// Retrieves the authorizations matching the specified parameters.
///
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
index 20f6fa86..a1321bf6 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
@@ -218,6 +218,40 @@ 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 authorization status.
+ /// 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 criteria.
+ ///
+ public override async Task> FindAsync(
+ [NotNull] string subject, [NotNull] string client,
+ [NotNull] string status, CancellationToken cancellationToken)
+ {
+
+ // 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)
+ => from authorization in authorizations.Include(authorization => authorization.Application)
+ where authorization.Subject == principal &&
+ authorization.Status == state
+ 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).ToListAsync(cancellationToken));
+ }
+
///
/// Retrieves the authorizations matching the specified parameters.
///