diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index c70f73e6..c2ba9021 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -201,6 +201,25 @@ namespace OpenIddict.Core
return Store.FindByIdAsync(identifier, cancellationToken);
}
+ ///
+ /// Retrieves the optional application identifier associated with an authorization.
+ ///
+ /// 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 application identifier associated with the authorization.
+ ///
+ public virtual Task GetApplicationIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ {
+ if (authorization == null)
+ {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ return Store.GetApplicationIdAsync(authorization, cancellationToken);
+ }
+
///
/// Executes the specified query.
///
@@ -338,6 +357,22 @@ namespace OpenIddict.Core
return Store.ListAsync(query, cancellationToken);
}
+ ///
+ /// Lists the ad-hoc authorizations that are marked as invalid or have no
+ /// valid token attached and that can be safely removed from the database.
+ ///
+ /// The number of results to return.
+ /// The number of results to skip.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns all the elements returned when executing the specified query.
+ ///
+ public virtual Task> ListInvalidAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken)
+ {
+ return Store.ListInvalidAsync(count, offset, cancellationToken);
+ }
+
///
/// Revokes an authorization.
///
@@ -360,6 +395,26 @@ namespace OpenIddict.Core
}
}
+ ///
+ /// Sets the application identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ {
+ if (authorization == null)
+ {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ await Store.SetApplicationIdAsync(authorization, identifier, cancellationToken);
+ await UpdateAsync(authorization, cancellationToken);
+ }
+
///
/// Updates an existing authorization.
///
diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
index 244a55da..540be6d3 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
@@ -236,6 +236,25 @@ namespace OpenIddict.Core
return Store.FindBySubjectAsync(subject, cancellationToken);
}
+ ///
+ /// Retrieves the optional application identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the application identifier associated with the token.
+ ///
+ public virtual Task GetApplicationIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ {
+ if (token == null)
+ {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ return Store.GetApplicationIdAsync(token, cancellationToken);
+ }
+
///
/// Executes the specified query.
///
@@ -490,6 +509,22 @@ namespace OpenIddict.Core
return Store.ListAsync(query, cancellationToken);
}
+ ///
+ /// Lists the tokens that are marked as expired or invalid
+ /// and that can be safely removed from the database.
+ ///
+ /// The number of results to return.
+ /// The number of results to skip.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns all the elements returned when executing the specified query.
+ ///
+ public virtual Task> ListInvalidAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken)
+ {
+ return Store.ListInvalidAsync(count, offset, cancellationToken);
+ }
+
///
/// Redeems a token.
///
@@ -533,42 +568,42 @@ namespace OpenIddict.Core
}
///
- /// Sets the authorization associated with a token.
+ /// Sets the application identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the authorization.
+ /// The unique identifier associated with the client application.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- public virtual async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ public virtual async Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
- await Store.SetAuthorizationAsync(token, identifier, cancellationToken);
+ await Store.SetApplicationIdAsync(token, identifier, cancellationToken);
await UpdateAsync(token, cancellationToken);
}
///
- /// Sets the client application associated with a token.
+ /// Sets the authorization identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the client application.
+ /// The unique identifier associated with the authorization.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- public virtual async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ public virtual async Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
- await Store.SetClientAsync(token, identifier, cancellationToken);
+ await Store.SetAuthorizationIdAsync(token, identifier, cancellationToken);
await UpdateAsync(token, cancellationToken);
}
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
index 6ce29b16..d4fb16d8 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
@@ -94,6 +94,17 @@ namespace OpenIddict.Core
///
Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken);
+ ///
+ /// Retrieves the optional application identifier associated with an authorization.
+ ///
+ /// 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 application identifier associated with the authorization.
+ ///
+ Task GetApplicationIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
+
///
/// Executes the specified query.
///
@@ -174,6 +185,30 @@ namespace OpenIddict.Core
///
Task> ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken);
+ ///
+ /// Lists the ad-hoc authorizations that are marked as invalid or have no
+ /// valid token attached and that can be safely removed from the database.
+ ///
+ /// The number of results to return.
+ /// The number of results to skip.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns all the elements returned when executing the specified query.
+ ///
+ Task> ListInvalidAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken);
+
+ ///
+ /// Sets the application identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ Task SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken);
+
///
/// Sets the status associated with an authorization.
///
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
index b5ed0a47..d35402f9 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
@@ -113,6 +113,17 @@ namespace OpenIddict.Core
///
Task> FindBySubjectAsync([NotNull] string subject, CancellationToken cancellationToken);
+ ///
+ /// Retrieves the optional application identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the application identifier associated with the token.
+ ///
+ Task GetApplicationIdAsync([NotNull] TToken token, CancellationToken cancellationToken);
+
///
/// Executes the specified query.
///
@@ -249,26 +260,39 @@ namespace OpenIddict.Core
Task> ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken);
///
- /// Sets the authorization associated with a token.
+ /// Lists the tokens that are marked as expired or invalid
+ /// and that can be safely removed from the database.
+ ///
+ /// The number of results to return.
+ /// The number of results to skip.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns all the elements returned when executing the specified query.
+ ///
+ Task> ListInvalidAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken);
+
+ ///
+ /// Sets the application identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the authorization.
+ /// The unique identifier associated with the client application.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
+ Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
///
- /// Sets the client application associated with a token.
+ /// Sets the authorization identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the client application.
+ /// The unique identifier associated with the authorization.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
+ Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
///
/// Sets the expiration date associated with a token.
diff --git a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
index 04e1af64..10a5b9f7 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
@@ -136,6 +136,35 @@ namespace OpenIddict.Core
return GetAsync(authorizations => authorizations.Where(authorization => authorization.Id.Equals(key)), cancellationToken);
}
+ ///
+ /// Retrieves the optional application identifier associated with an authorization.
+ ///
+ /// 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 application identifier associated with the authorization.
+ ///
+ public virtual async Task GetApplicationIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ {
+ if (authorization == null)
+ {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ if (authorization.Application != null)
+ {
+ return ConvertIdentifierToString(authorization.Application.Id);
+ }
+
+ var key = await GetAsync(authorizations =>
+ from element in authorizations
+ where element.Id.Equals(authorization.Id)
+ select element.Application.Id, cancellationToken);
+
+ return ConvertIdentifierToString(key);
+ }
+
///
/// Executes the specified query.
///
@@ -263,6 +292,55 @@ namespace OpenIddict.Core
///
public abstract Task> ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken);
+ ///
+ /// Lists the ad-hoc authorizations that are marked as invalid or have no
+ /// valid token attached and that can be safely removed from the database.
+ ///
+ /// The number of results to return.
+ /// The number of results to skip.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns all the elements returned when executing the specified query.
+ ///
+ public virtual Task> ListInvalidAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken)
+ {
+ IQueryable Query(IQueryable authorizations)
+ {
+ var query = (from authorization in authorizations
+ where authorization.Status != OpenIddictConstants.Statuses.Valid ||
+ (authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc &&
+ !authorization.Tokens.Any(token => token.Status == OpenIddictConstants.Statuses.Valid))
+ orderby authorization.Id
+ select authorization).AsQueryable();
+
+ if (offset.HasValue)
+ {
+ query = query.Skip(offset.Value);
+ }
+
+ if (count.HasValue)
+ {
+ query = query.Take(count.Value);
+ }
+
+ return query;
+ }
+
+ return ListAsync(Query, cancellationToken);
+ }
+
+ ///
+ /// Sets the application identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public abstract Task SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken);
+
///
/// Sets the status associated with an authorization.
///
diff --git a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
index b5c468ed..c6dc11d4 100644
--- a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
@@ -174,6 +174,35 @@ namespace OpenIddict.Core
///
public abstract Task GetAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken);
+ ///
+ /// Retrieves the optional application identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the application identifier associated with the token.
+ ///
+ public virtual async Task GetApplicationIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ {
+ if (token == null)
+ {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ if (token.Application != null)
+ {
+ return ConvertIdentifierToString(token.Application.Id);
+ }
+
+ var key = await GetAsync(tokens =>
+ from element in tokens
+ where element.Id.Equals(token.Id)
+ select element.Application.Id, cancellationToken);
+
+ return ConvertIdentifierToString(key);
+ }
+
///
/// Retrieves the optional authorization identifier associated with a token.
///
@@ -400,7 +429,44 @@ namespace OpenIddict.Core
public abstract Task> ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken);
///
- /// Sets the authorization associated with a token.
+ /// Lists the tokens that are marked as expired or invalid
+ /// and that can be safely removed from the database.
+ ///
+ /// The number of results to return.
+ /// The number of results to skip.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns all the elements returned when executing the specified query.
+ ///
+ public virtual Task> ListInvalidAsync([CanBeNull] int? count, [CanBeNull] int? offset, CancellationToken cancellationToken)
+ {
+ IQueryable Query(IQueryable tokens)
+ {
+ var query = (from token in tokens
+ where token.ExpirationDate < DateTimeOffset.UtcNow ||
+ token.Status != OpenIddictConstants.Statuses.Valid
+ orderby token.Id
+ select token).AsQueryable();
+
+ if (offset.HasValue)
+ {
+ query = query.Skip(offset.Value);
+ }
+
+ if (count.HasValue)
+ {
+ query = query.Take(count.Value);
+ }
+
+ return query;
+ }
+
+ return ListAsync(Query, cancellationToken);
+ }
+
+ ///
+ /// Sets the authorization identifier associated with a token.
///
/// The token.
/// The unique identifier associated with the authorization.
@@ -408,10 +474,10 @@ namespace OpenIddict.Core
///
/// A that can be used to monitor the asynchronous operation.
///
- public abstract Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
+ public abstract Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
///
- /// Sets the client application associated with a token.
+ /// Sets the application identifier associated with a token.
///
/// The token.
/// The unique identifier associated with the client application.
@@ -419,7 +485,7 @@ namespace OpenIddict.Core
///
/// A that can be used to monitor the asynchronous operation.
///
- public abstract Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
+ public abstract Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
///
/// Sets the expiration date associated with a token.
diff --git a/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
index d0f7115d..fb008fce 100644
--- a/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
+++ b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
@@ -210,7 +210,8 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Entity()
.HasMany(application => application.Tokens)
.WithOptional(token => token.Application)
- .Map(association => association.MapKey("ApplicationId"));
+ .Map(association => association.MapKey("ApplicationId"))
+ .WillCascadeOnDelete();
builder.Entity()
.ToTable("OpenIddictApplications");
@@ -240,7 +241,8 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Entity()
.HasMany(application => application.Tokens)
.WithOptional(token => token.Authorization)
- .Map(association => association.MapKey("AuthorizationId"));
+ .Map(association => association.MapKey("AuthorizationId"))
+ .WillCascadeOnDelete();
builder.Entity()
.ToTable("OpenIddictAuthorizations");
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
index 326c1fda..ae9a1652 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
@@ -81,6 +81,16 @@ namespace OpenIddict.EntityFramework
///
protected DbSet Applications => Context.Set();
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Authorizations => Context.Set();
+
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Tokens => Context.Set();
+
///
/// Determines the number of applications that match the specified query.
///
@@ -171,16 +181,48 @@ namespace OpenIddict.EntityFramework
///
/// A that can be used to monitor the asynchronous operation.
///
- public override Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken)
+ public override async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
+ Task ListAuthorizationsAsync()
+ {
+ return (from authorization in Authorizations.Include(authorization => authorization.Tokens)
+ where authorization.Application.Id.Equals(application.Id)
+ select authorization).ToArrayAsync(cancellationToken);
+ }
+
+ Task ListTokensAsync()
+ {
+ return (from token in Tokens
+ where token.Application.Id.Equals(application.Id)
+ select token).ToArrayAsync(cancellationToken);
+ }
+
+ // Remove all the authorizations associated with the application and
+ // the tokens attached to these implicit or explicit authorizations.
+ foreach (var authorization in await ListAuthorizationsAsync())
+ {
+ foreach (var token in authorization.Tokens)
+ {
+ Tokens.Remove(token);
+ }
+
+ Authorizations.Remove(authorization);
+ }
+
+ // Remove all the tokens associated with the application.
+ foreach (var token in await ListTokensAsync())
+ {
+ Tokens.Remove(token);
+ }
+
Applications.Remove(application);
- return Context.SaveChangesAsync(cancellationToken);
+ await Context.SaveChangesAsync(cancellationToken);
}
///
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
index d40f511f..f87e75f0 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
@@ -86,6 +86,11 @@ namespace OpenIddict.EntityFramework
///
protected DbSet Authorizations => Context.Set();
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Tokens => Context.Set();
+
///
/// Determines the number of authorizations that match the specified query.
///
@@ -178,16 +183,29 @@ namespace OpenIddict.EntityFramework
///
/// A that can be used to monitor the asynchronous operation.
///
- public override Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ public override async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
+ Task ListTokensAsync()
+ {
+ return (from token in Tokens
+ where token.Application.Id.Equals(authorization.Id)
+ select token).ToArrayAsync(cancellationToken);
+ }
+
+ // Remove all the tokens associated with the application.
+ foreach (var token in await ListTokensAsync())
+ {
+ Tokens.Remove(token);
+ }
+
Authorizations.Remove(authorization);
-
- return Context.SaveChangesAsync(cancellationToken);
+
+ await Context.SaveChangesAsync(cancellationToken);
}
///
@@ -209,6 +227,38 @@ namespace OpenIddict.EntityFramework
return Authorizations.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
}
+ ///
+ /// Retrieves the optional application identifier associated with an authorization.
+ ///
+ /// 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 application identifier associated with the authorization.
+ ///
+ public override async Task GetApplicationIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ {
+ if (authorization == null)
+ {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ // If the application is not attached to the authorization instance (which is expected
+ // if the token was retrieved using the default FindBy*Async APIs as they don't
+ // eagerly load the application from the database), try to load it manually.
+ if (authorization.Application == null)
+ {
+ return ConvertIdentifierToString(
+ await Context.Entry(authorization)
+ .Reference(entry => entry.Application)
+ .Query()
+ .Select(application => application.Id)
+ .FirstOrDefaultAsync());
+ }
+
+ return ConvertIdentifierToString(authorization.Application.Id);
+ }
+
///
/// Executes the specified query.
///
@@ -249,6 +299,47 @@ namespace OpenIddict.EntityFramework
return ImmutableArray.Create(await query.Invoke(Authorizations).ToArrayAsync(cancellationToken));
}
+ ///
+ /// Sets the application identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public override async Task SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ {
+ if (authorization == null)
+ {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ if (!string.IsNullOrEmpty(identifier))
+ {
+ var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
+ if (application == null)
+ {
+ throw new InvalidOperationException("The application associated with the authorization cannot be found.");
+ }
+
+ authorization.Application = application;
+ }
+
+ else
+ {
+ var key = await GetIdAsync(authorization, cancellationToken);
+
+ // Try to retrieve the application associated with the authorization.
+ // If none can be found, assume that no application is attached.
+ var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (application != null)
+ {
+ application.Authorizations.Remove(authorization);
+ }
+ }
+ }
+
///
/// Updates an existing authorization.
///
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
index ad4c2474..5d7c4f06 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
@@ -223,6 +223,38 @@ namespace OpenIddict.EntityFramework
return Tokens.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
}
+ ///
+ /// Retrieves the optional application identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the application identifier associated with the token.
+ ///
+ public override async Task GetApplicationIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ {
+ if (token == null)
+ {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ // If the application is not attached to the token instance (which is expected
+ // if the token was retrieved using the default FindBy*Async APIs as they don't
+ // eagerly load the application from the database), try to load it manually.
+ if (token.Application == null)
+ {
+ return ConvertIdentifierToString(
+ await Context.Entry(token)
+ .Reference(entry => entry.Application)
+ .Query()
+ .Select(application => application.Id)
+ .FirstOrDefaultAsync());
+ }
+
+ return ConvertIdentifierToString(token.Application.Id);
+ }
+
///
/// Executes the specified query.
///
@@ -243,6 +275,38 @@ namespace OpenIddict.EntityFramework
return query.Invoke(Tokens).SingleOrDefaultAsync(cancellationToken);
}
+ ///
+ /// Retrieves the optional authorization identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorization identifier associated with the token.
+ ///
+ public override async Task GetAuthorizationIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
+ {
+ if (token == null)
+ {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ // If the authorization is not attached to the token instance (which is expected
+ // if the token was retrieved using the default FindBy*Async APIs as they don't
+ // eagerly load the authorization from the database), try to load it manually.
+ if (token.Authorization == null)
+ {
+ return ConvertIdentifierToString(
+ await Context.Entry(token)
+ .Reference(entry => entry.Authorization)
+ .Query()
+ .Select(authorization => authorization.Id)
+ .FirstOrDefaultAsync());
+ }
+
+ return ConvertIdentifierToString(token.Authorization.Id);
+ }
+
///
/// Executes the specified query.
///
@@ -264,15 +328,15 @@ namespace OpenIddict.EntityFramework
}
///
- /// Sets the authorization associated with a token.
+ /// Sets the application identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the authorization.
+ /// The unique identifier associated with the client application.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- public override async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ public override async Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -281,39 +345,39 @@ namespace OpenIddict.EntityFramework
if (!string.IsNullOrEmpty(identifier))
{
- var authorization = await Authorizations.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
- if (authorization == null)
+ var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
+ if (application == null)
{
- throw new InvalidOperationException("The authorization associated with the token cannot be found.");
+ throw new InvalidOperationException("The application associated with the token cannot be found.");
}
- token.Authorization = authorization;
+ token.Application = application;
}
else
{
var key = await GetIdAsync(token, cancellationToken);
- // Try to retrieve the authorization associated with the token.
- // If none can be found, assume that no authorization is attached.
- var authorization = await Authorizations.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
- if (authorization != null)
+ // Try to retrieve the application associated with the token.
+ // If none can be found, assume that no application is attached.
+ var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (application != null)
{
- authorization.Tokens.Remove(token);
+ application.Tokens.Remove(token);
}
}
}
///
- /// Sets the client application associated with a token.
+ /// Sets the authorization identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the client application.
+ /// The unique identifier associated with the authorization.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- public override async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ public override async Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -322,25 +386,25 @@ namespace OpenIddict.EntityFramework
if (!string.IsNullOrEmpty(identifier))
{
- var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
- if (application == null)
+ var authorization = await Authorizations.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
+ if (authorization == null)
{
- throw new InvalidOperationException("The application associated with the token cannot be found.");
+ throw new InvalidOperationException("The authorization associated with the token cannot be found.");
}
- token.Application = application;
+ token.Authorization = authorization;
}
else
{
var key = await GetIdAsync(token, cancellationToken);
- // Try to retrieve the application associated with the token.
- // If none can be found, assume that no application is attached.
- var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
- if (application != null)
+ // Try to retrieve the authorization associated with the token.
+ // If none can be found, assume that no authorization is attached.
+ var authorization = await Authorizations.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (authorization != null)
{
- application.Tokens.Remove(token);
+ authorization.Tokens.Remove(token);
}
}
}
diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
index 379764ce..12d82a5b 100644
--- a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
+++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
@@ -11,6 +11,7 @@ using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore;
@@ -243,7 +244,8 @@ namespace Microsoft.Extensions.DependencyInjection
entity.HasMany(application => application.Tokens)
.WithOne(token => token.Application)
.HasForeignKey("ApplicationId")
- .IsRequired(required: false);
+ .IsRequired(required: false)
+ .OnDelete(DeleteBehavior.Cascade);
entity.ToTable("OpenIddictApplications");
});
@@ -266,10 +268,11 @@ namespace Microsoft.Extensions.DependencyInjection
entity.Property(authorization => authorization.Type)
.IsRequired();
- entity.HasMany(application => application.Tokens)
+ entity.HasMany(authorization => authorization.Tokens)
.WithOne(token => token.Authorization)
.HasForeignKey("AuthorizationId")
- .IsRequired(required: false);
+ .IsRequired(required: false)
+ .OnDelete(DeleteBehavior.Cascade);
entity.ToTable("OpenIddictAuthorizations");
});
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
index aab9aa0a..133704a6 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
@@ -81,6 +81,16 @@ namespace OpenIddict.EntityFrameworkCore
///
protected DbSet Applications => Context.Set();
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Authorizations => Context.Set();
+
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Tokens => Context.Set();
+
///
/// Determines the number of applications that match the specified query.
///
@@ -171,16 +181,48 @@ namespace OpenIddict.EntityFrameworkCore
///
/// A that can be used to monitor the asynchronous operation.
///
- public override Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken)
+ public override async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
+ Task ListAuthorizationsAsync()
+ {
+ return (from authorization in Authorizations.Include(authorization => authorization.Tokens)
+ where authorization.Application.Id.Equals(application.Id)
+ select authorization).ToArrayAsync(cancellationToken);
+ }
+
+ Task ListTokensAsync()
+ {
+ return (from token in Tokens
+ where token.Application.Id.Equals(application.Id)
+ select token).ToArrayAsync(cancellationToken);
+ }
+
+ // Remove all the authorizations associated with the application and
+ // the tokens attached to these implicit or explicit authorizations.
+ foreach (var authorization in await ListAuthorizationsAsync())
+ {
+ foreach (var token in authorization.Tokens)
+ {
+ Context.Remove(token);
+ }
+
+ Context.Remove(authorization);
+ }
+
+ // Remove all the tokens associated with the application.
+ foreach (var token in await ListTokensAsync())
+ {
+ Context.Remove(token);
+ }
+
Context.Remove(application);
- return Context.SaveChangesAsync(cancellationToken);
+ await Context.SaveChangesAsync(cancellationToken);
}
///
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
index a3d5e8af..eb7a22db 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
@@ -86,6 +86,11 @@ namespace OpenIddict.EntityFrameworkCore
///
protected DbSet Authorizations => Context.Set();
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Tokens => Context.Set();
+
///
/// Determines the number of authorizations that match the specified query.
///
@@ -180,16 +185,29 @@ namespace OpenIddict.EntityFrameworkCore
///
/// A that can be used to monitor the asynchronous operation.
///
- public override Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
+ public override async Task DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
+ Task ListTokensAsync()
+ {
+ return (from token in Tokens
+ where token.Application.Id.Equals(authorization.Id)
+ select token).ToArrayAsync(cancellationToken);
+ }
+
+ // Remove all the tokens associated with the application.
+ foreach (var token in await ListTokensAsync())
+ {
+ Context.Remove(token);
+ }
+
Context.Remove(authorization);
- return Context.SaveChangesAsync(cancellationToken);
+ await Context.SaveChangesAsync(cancellationToken);
}
///
@@ -232,6 +250,49 @@ namespace OpenIddict.EntityFrameworkCore
return ImmutableArray.Create(await query.Invoke(Authorizations).ToArrayAsync(cancellationToken));
}
+ ///
+ /// Sets the application identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public override async Task SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ {
+ if (authorization == null)
+ {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ if (!string.IsNullOrEmpty(identifier))
+ {
+ var key = ConvertIdentifierFromString(identifier);
+
+ var application = await Applications.SingleOrDefaultAsync(element => element.Id.Equals(key), cancellationToken);
+ if (application == null)
+ {
+ throw new InvalidOperationException("The application associated with the authorization cannot be found.");
+ }
+
+ authorization.Application = application;
+ }
+
+ else
+ {
+ var key = await GetIdAsync(authorization, cancellationToken);
+
+ // Try to retrieve the application associated with the authorization.
+ // If none can be found, assume that no application is attached.
+ var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (application != null)
+ {
+ application.Authorizations.Remove(authorization);
+ }
+ }
+ }
+
///
/// Updates an existing authorization.
///
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
index 2254b6b0..46a97c04 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
@@ -249,15 +249,15 @@ namespace OpenIddict.EntityFrameworkCore
}
///
- /// Sets the authorization associated with a token.
+ /// Sets the application identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the authorization.
+ /// The unique identifier associated with the client application.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- public override async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ public override async Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -268,39 +268,39 @@ namespace OpenIddict.EntityFrameworkCore
{
var key = ConvertIdentifierFromString(identifier);
- var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Id.Equals(key));
- if (authorization == null)
+ var application = await Applications.SingleOrDefaultAsync(element => element.Id.Equals(key), cancellationToken);
+ if (application == null)
{
- throw new InvalidOperationException("The authorization associated with the token cannot be found.");
+ throw new InvalidOperationException("The application associated with the token cannot be found.");
}
- token.Authorization = authorization;
+ token.Application = application;
}
else
{
var key = await GetIdAsync(token, cancellationToken);
- // Try to retrieve the authorization associated with the token.
- // If none can be found, assume that no authorization is attached.
- var authorization = await Authorizations.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
- if (authorization != null)
+ // Try to retrieve the application associated with the token.
+ // If none can be found, assume that no application is attached.
+ var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (application != null)
{
- authorization.Tokens.Remove(token);
+ application.Tokens.Remove(token);
}
}
}
///
- /// Sets the client application associated with a token.
+ /// Sets the authorization identifier associated with a token.
///
/// The token.
- /// The unique identifier associated with the client application.
+ /// The unique identifier associated with the authorization.
/// The that can be used to abort the operation.
///
/// A that can be used to monitor the asynchronous operation.
///
- public override async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
+ public override async Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
@@ -311,25 +311,25 @@ namespace OpenIddict.EntityFrameworkCore
{
var key = ConvertIdentifierFromString(identifier);
- var application = await Applications.SingleOrDefaultAsync(element => element.Id.Equals(key));
- if (application == null)
+ var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Id.Equals(key), cancellationToken);
+ if (authorization == null)
{
- throw new InvalidOperationException("The application associated with the token cannot be found.");
+ throw new InvalidOperationException("The authorization associated with the token cannot be found.");
}
- token.Application = application;
+ token.Authorization = authorization;
}
else
{
var key = await GetIdAsync(token, cancellationToken);
- // Try to retrieve the application associated with the token.
- // If none can be found, assume that no application is attached.
- var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
- if (application != null)
+ // Try to retrieve the authorization associated with the token.
+ // If none can be found, assume that no authorization is attached.
+ var authorization = await Authorizations.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (authorization != null)
{
- application.Tokens.Remove(token);
+ authorization.Tokens.Remove(token);
}
}
}
diff --git a/src/OpenIddict/OpenIddictProvider.Helpers.cs b/src/OpenIddict/OpenIddictProvider.Helpers.cs
index e3c70807..846cfb92 100644
--- a/src/OpenIddict/OpenIddictProvider.Helpers.cs
+++ b/src/OpenIddict/OpenIddictProvider.Helpers.cs
@@ -206,12 +206,11 @@ namespace OpenIddict
ticket.SetTokenId(identifier);
// Dynamically set the creation and expiration dates.
- ticket.Properties.IssuedUtc = await tokens.GetCreationDateAsync(token, context.RequestAborted);
- ticket.Properties.ExpiresUtc = await tokens.GetExpirationDateAsync(token, context.RequestAborted);
+ ticket.Properties.IssuedUtc = descriptor.CreationDate;
+ ticket.Properties.ExpiresUtc = descriptor.ExpirationDate;
// Restore the authorization identifier using the identifier attached with the database entry.
- ticket.SetProperty(OpenIddictConstants.Properties.AuthorizationId,
- await tokens.GetAuthorizationIdAsync(token, context.RequestAborted));
+ ticket.SetProperty(OpenIddictConstants.Properties.AuthorizationId, descriptor.AuthorizationId);
if (!string.IsNullOrEmpty(result))
{
diff --git a/src/OpenIddict/OpenIddictProvider.Signin.cs b/src/OpenIddict/OpenIddictProvider.Signin.cs
index 72c38136..ef04be79 100644
--- a/src/OpenIddict/OpenIddictProvider.Signin.cs
+++ b/src/OpenIddict/OpenIddictProvider.Signin.cs
@@ -29,8 +29,8 @@ namespace OpenIddict
// the OpenID Connect server middleware allows creating authentication tickets
// that are completely disconnected from the original code or refresh token ticket.
// This scenario is deliberately not supported in OpenIddict and all the tickets
- // must be linked. To ensure the properties are preserved from an authorization code
- // or a refresh token to the new ticket, they are manually restored if necessary.
+ // must be linked. To ensure the properties are flowed from the authorization code
+ // or the refresh token to the new ticket, they are manually restored if necessary.
// Retrieve the original authentication ticket from the request properties.
var ticket = context.Request.GetProperty(