diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs index 5a64369f..210dc783 100644 --- a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs @@ -141,6 +141,17 @@ namespace OpenIddict.Abstractions /// Task> FindAsync([NotNull] string subject, [NotNull] string client, [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default); + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// 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 application. + /// + Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken = default); + /// /// Retrieves an authorization using its unique identifier. /// diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs index 77324e8a..211936a3 100644 --- a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs @@ -125,6 +125,17 @@ namespace OpenIddict.Abstractions [NotNull] string status, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken); + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// 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 application. + /// + Task> FindByApplicationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + /// /// Retrieves an authorization using its unique identifier. /// diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs index 1a42933d..772f4921 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs @@ -446,6 +446,26 @@ namespace OpenIddict.Core builder.ToImmutable(); } + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// 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 application. + /// + public virtual Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken = default) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + return Store.FindByApplicationIdAsync(identifier, cancellationToken); + } + /// /// Retrieves an authorization using its unique identifier. /// @@ -1082,6 +1102,9 @@ namespace OpenIddict.Core async Task> IOpenIddictAuthorizationManager.FindAsync(string subject, string client, string status, string type, ImmutableArray scopes, CancellationToken cancellationToken) => (await FindAsync(subject, client, status, type, scopes, cancellationToken)).CastArray(); + async Task> IOpenIddictAuthorizationManager.FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken) + => (await FindByApplicationIdAsync(identifier, cancellationToken)).CastArray(); + async Task IOpenIddictAuthorizationManager.FindByIdAsync(string identifier, CancellationToken cancellationToken) => await FindByIdAsync(identifier, cancellationToken); diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs index e60df750..b9962550 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictApplication.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -24,6 +25,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict application. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")] public class OpenIddictApplication where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs index 7ebfd69d..d7996a7e 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictAuthorization.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -24,6 +25,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict authorization. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictAuthorization where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs index 30967531..91d487f3 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictScope.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -23,6 +24,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict scope. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")] public class OpenIddictScope where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs b/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs index 378f8f5c..411b9f74 100644 --- a/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs +++ b/src/OpenIddict.EntityFramework.Models/OpenIddictToken.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFramework.Models { @@ -23,6 +24,7 @@ namespace OpenIddict.EntityFramework.Models /// /// Represents an OpenIddict token. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictToken where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs index 69dfe095..fc20a7cc 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs @@ -378,6 +378,31 @@ namespace OpenIddict.EntityFramework builder.ToImmutable(); } + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// 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 application. + /// + public virtual async Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + var key = ConvertIdentifierFromString(identifier); + + return ImmutableArray.CreateRange( + await (from authorization in Authorizations.Include(authorization => authorization.Application) + where authorization.Application.Id.Equals(key) + select authorization).ToListAsync(cancellationToken)); + } + /// /// Retrieves an authorization using its unique identifier. /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs index e5306535..c452d1a9 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictApplication.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -31,6 +32,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict application. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")] public class OpenIddictApplication where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs index 67cc0272..6f4f210e 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictAuthorization.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -31,6 +32,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict authorization. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictAuthorization where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs index b28ba8a0..0c739c04 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictScope.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -23,6 +24,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict scope. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")] public class OpenIddictScope where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs index 92cca5d3..5dc84464 100644 --- a/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs +++ b/src/OpenIddict.EntityFrameworkCore.Models/OpenIddictToken.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; namespace OpenIddict.EntityFrameworkCore.Models { @@ -31,6 +32,7 @@ namespace OpenIddict.EntityFrameworkCore.Models /// /// Represents an OpenIddict token. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictToken where TKey : IEquatable { /// diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs index e6521006..e0258e75 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs @@ -449,6 +449,44 @@ namespace OpenIddict.EntityFrameworkCore builder.ToImmutable(); } + /// + /// Exposes a compiled query allowing to retrieve the list of + /// authorizations corresponding to the specified application identifier. + /// + private static readonly Func> FindByApplicationId = + // 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 compiled query uses an explicit join before applying the equality check. + // See https://github.com/openiddict/openiddict-core/issues/499 for more information. + EF.CompileAsyncQuery((TContext context, TKey identifier) => + from authorization in context.Set() + .Include(authorization => authorization.Application) + .AsTracking() + join application in context.Set().AsTracking() on authorization.Application.Id equals application.Id + where application.Id.Equals(identifier) + select authorization); + + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// 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 application. + /// + public virtual async Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + return ImmutableArray.CreateRange(await FindByApplicationId(Context, + ConvertIdentifierFromString(identifier)).ToListAsync(cancellationToken)); + } + /// /// Exposes a compiled query allowing to retrieve an authorization using its unique identifier. /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs b/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs index 9b4893f0..750660b5 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictApplication.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict application. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")] public class OpenIddictApplication { /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs b/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs index 57abefb3..6eba16fa 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictAuthorization.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict authorization. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictAuthorization { /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs b/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs index 8b6909a7..c1cb4939 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictScope.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict scope. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")] public class OpenIddictScope { /// diff --git a/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs b/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs index c212b5d5..65ed4398 100644 --- a/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs +++ b/src/OpenIddict.MongoDb.Models/OpenIddictToken.cs @@ -5,6 +5,7 @@ */ using System; +using System.Diagnostics; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -13,6 +14,7 @@ namespace OpenIddict.MongoDb.Models /// /// Represents an OpenIddict token. /// + [DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")] public class OpenIddictToken { /// diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs index 501999c7..92e9117c 100644 --- a/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.MongoDb/Stores/OpenIddictAuthorizationStore.cs @@ -314,6 +314,30 @@ namespace OpenIddict.MongoDb Enumerable.All(scopes, scope => authorization.Scopes.Contains(scope))).ToListAsync(cancellationToken)); } + /// + /// Retrieves the list of authorizations corresponding to the specified application identifier. + /// + /// The application identifier associated with the authorizations. + /// 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 application. + /// + public virtual async Task> FindByApplicationIdAsync( + [NotNull] string identifier, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); + } + + var database = await Context.GetDatabaseAsync(cancellationToken); + var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName); + + return ImmutableArray.CreateRange(await collection.Find(authorization => + authorization.ApplicationId == ObjectId.Parse(identifier)).ToListAsync(cancellationToken)); + } + /// /// Retrieves an authorization using its unique identifier. ///