diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index 96989e46..428000b2 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -173,16 +173,17 @@ namespace OpenIddict.Core
}
///
- /// Retrieves an authorization using its associated subject/client.
+ /// Retrieves the authorizations corresponding to the specified
+ /// subject and associated with the application identifier.
///
/// The subject associated with the authorization.
/// The client 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 corresponding to the subject/client.
+ /// 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))
{
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
index d4fb16d8..6b54e6df 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
@@ -72,16 +72,17 @@ namespace OpenIddict.Core
Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
///
- /// Retrieves an authorization using its associated subject/client.
+ /// Retrieves the authorizations corresponding to the specified
+ /// subject and associated with the application identifier.
///
/// The subject associated with the authorization.
/// The client 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 corresponding to the subject/client.
+ /// 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 an authorization using its unique identifier.
diff --git a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs
index b0778bb8..ef80e777 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs
@@ -100,9 +100,16 @@ namespace OpenIddict.Core
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
- var key = ConvertIdentifierFromString(identifier);
+ IQueryable Query(IQueryable applications)
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return from application in applications
+ where application.Id.Equals(key)
+ select application;
+ }
- return GetAsync(applications => applications.Where(application => application.Id.Equals(key)), cancellationToken);
+ return GetAsync(Query, cancellationToken);
}
///
@@ -121,7 +128,14 @@ namespace OpenIddict.Core
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
- return GetAsync(applications => applications.Where(application => application.ClientId.Equals(identifier)), cancellationToken);
+ IQueryable Query(IQueryable applications)
+ {
+ return from application in applications
+ where application.ClientId == identifier
+ select application;
+ }
+
+ return GetAsync(Query, cancellationToken);
}
///
diff --git a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
index 10a5b9f7..551745ce 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
@@ -85,16 +85,17 @@ namespace OpenIddict.Core
public abstract Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
///
- /// Retrieves an authorization using its associated subject/client.
+ /// Retrieves the authorizations corresponding to the specified
+ /// subject and associated with the application identifier.
///
/// The subject associated with the authorization.
/// The client 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 corresponding to the subject/client.
+ /// 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))
{
@@ -106,13 +107,18 @@ namespace OpenIddict.Core
throw new ArgumentException("The client cannot be null or empty.", nameof(client));
}
- var key = ConvertIdentifierFromString(client);
+ IQueryable Query(IQueryable authorizations)
+ {
+ var key = ConvertIdentifierFromString(client);
- return GetAsync(authorizations =>
- from authorization in authorizations
- where authorization.Application.Id.Equals(key)
- where authorization.Subject == subject
- select authorization, cancellationToken);
+ return from authorization in authorizations
+ where authorization.Application != null
+ where authorization.Application.Id.Equals(key)
+ where authorization.Subject == subject
+ select authorization;
+ }
+
+ return ListAsync(Query, cancellationToken);
}
///
@@ -131,9 +137,16 @@ namespace OpenIddict.Core
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
- var key = ConvertIdentifierFromString(identifier);
+ IQueryable Query(IQueryable authorizations)
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return from authorization in authorizations
+ where authorization.Id.Equals(key)
+ select authorization;
+ }
- return GetAsync(authorizations => authorizations.Where(authorization => authorization.Id.Equals(key)), cancellationToken);
+ return GetAsync(Query, cancellationToken);
}
///
diff --git a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
index ff786bf4..0030bbb0 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
@@ -98,9 +98,17 @@ namespace OpenIddict.Core
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
- var key = ConvertIdentifierFromString(identifier);
+ IQueryable Query(IQueryable tokens)
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return from token in tokens
+ where token.Application != null
+ where token.Application.Id.Equals(key)
+ select token;
+ }
- return ListAsync(tokens => tokens.Where(token => token.Application.Id.Equals(key)), cancellationToken);
+ return ListAsync(Query, cancellationToken);
}
///
@@ -119,9 +127,17 @@ namespace OpenIddict.Core
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
- var key = ConvertIdentifierFromString(identifier);
+ IQueryable Query(IQueryable tokens)
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return from token in tokens
+ where token.Authorization != null
+ where token.Authorization.Id.Equals(key)
+ select token;
+ }
- return ListAsync(tokens => tokens.Where(token => token.Authorization.Id.Equals(key)), cancellationToken);
+ return ListAsync(Query, cancellationToken);
}
///
@@ -140,7 +156,14 @@ namespace OpenIddict.Core
throw new ArgumentException("The hash cannot be null or empty.", nameof(hash));
}
- return GetAsync(tokens => tokens.Where(token => token.Hash == hash), cancellationToken);
+ IQueryable Query(IQueryable tokens)
+ {
+ return from token in tokens
+ where token.Hash == hash
+ select token;
+ }
+
+ return GetAsync(Query, cancellationToken);
}
///
@@ -159,9 +182,16 @@ namespace OpenIddict.Core
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
- var key = ConvertIdentifierFromString(identifier);
+ IQueryable Query(IQueryable tokens)
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return from token in tokens
+ where token.Id.Equals(key)
+ select token;
+ }
- return GetAsync(tokens => tokens.Where(token => token.Id.Equals(key)), cancellationToken);
+ return GetAsync(Query, cancellationToken);
}
///
@@ -180,7 +210,14 @@ namespace OpenIddict.Core
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
- return ListAsync(tokens => tokens.Where(token => token.Subject == subject), cancellationToken);
+ IQueryable Query(IQueryable tokens)
+ {
+ return from token in tokens
+ where token.Subject == subject
+ select token;
+ }
+
+ return ListAsync(Query, cancellationToken);
}
///
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
index 1778ca94..1c77d3f8 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
@@ -369,7 +369,7 @@ namespace OpenIddict.EntityFramework
// Bind the authorization to the specified application, if applicable.
if (!string.IsNullOrEmpty(descriptor.ApplicationId))
{
- var application = await Applications.FindAsync(new object[] { ConvertIdentifierFromString(descriptor.ApplicationId) }, cancellationToken);
+ var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(descriptor.ApplicationId));
if (application == null)
{
throw new InvalidOperationException("The application associated with the authorization cannot be found.");
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
index 1e6135b8..af7d64f8 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
@@ -430,7 +430,7 @@ namespace OpenIddict.EntityFramework
// Bind the token to the specified client application, if applicable.
if (!string.IsNullOrEmpty(descriptor.ApplicationId))
{
- var application = await Applications.FindAsync(new object[] { ConvertIdentifierFromString(descriptor.ApplicationId) }, cancellationToken);
+ var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(descriptor.ApplicationId));
if (application == null)
{
throw new InvalidOperationException("The application associated with the token cannot be found.");
@@ -442,7 +442,7 @@ namespace OpenIddict.EntityFramework
// Bind the token to the specified authorization, if applicable.
if (!string.IsNullOrEmpty(descriptor.AuthorizationId))
{
- var authorization = await Authorizations.FindAsync(new object[] { ConvertIdentifierFromString(descriptor.AuthorizationId) }, cancellationToken);
+ var authorization = await Authorizations.FindAsync(cancellationToken, ConvertIdentifierFromString(descriptor.AuthorizationId));
if (authorization == null)
{
throw new InvalidOperationException("The authorization associated with the token cannot be found.");
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
index 89006cd9..96aa244d 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
@@ -187,6 +187,48 @@ namespace OpenIddict.EntityFrameworkCore
await Context.SaveChangesAsync(cancellationToken);
}
+ ///
+ /// Retrieves the authorizations corresponding to the specified
+ /// subject and associated with the application identifier.
+ ///
+ /// The subject associated with the authorization.
+ /// The client 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, 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));
+ }
+
+ // 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)
+ {
+ var key = ConvertIdentifierFromString(client);
+
+ return from authorization in authorizations
+ where authorization.Subject == subject
+ join application in applications on authorization.Application.Id equals application.Id
+ where application.Id.Equals(key)
+ select authorization;
+ }
+
+ return ImmutableArray.Create(await Query(Authorizations, Applications).ToArrayAsync(cancellationToken));
+ }
+
///
/// Executes the specified query.
///
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
index 51ac5efc..352d7269 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
@@ -172,6 +172,74 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken);
}
+ ///
+ /// Retrieves the list of tokens corresponding to the specified application identifier.
+ ///
+ /// The application identifier associated with the tokens.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the tokens corresponding to the specified application.
+ ///
+ public override async Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(identifier))
+ {
+ throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
+ }
+
+ // Note: due to a bug in Entity Framework Core's query visitor, the tokens can't be
+ // filtered using token.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 applications, IQueryable tokens)
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return from token in tokens
+ join application in applications on token.Application.Id equals application.Id
+ where application.Id.Equals(key)
+ select token;
+ }
+
+ return ImmutableArray.Create(await Query(Applications, Tokens).ToArrayAsync(cancellationToken));
+ }
+
+ ///
+ /// Retrieves the list of tokens corresponding to the specified authorization identifier.
+ ///
+ /// The authorization identifier associated with the tokens.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the tokens corresponding to the specified authorization.
+ ///
+ public override async Task> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(identifier))
+ {
+ throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
+ }
+
+ // Note: due to a bug in Entity Framework Core's query visitor, the tokens can't be
+ // filtered using token.Authorization.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 tokens)
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return from token in tokens
+ join authorization in authorizations on token.Authorization.Id equals authorization.Id
+ where authorization.Id.Equals(key)
+ select token;
+ }
+
+ return ImmutableArray.Create(await Query(Authorizations, Tokens).ToArrayAsync(cancellationToken));
+ }
+
///
/// Executes the specified query.
///