diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index 18be7633..71a2bf68 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -531,6 +531,49 @@ namespace OpenIddict.Core
return Store.ListInvalidAsync(count, offset, cancellationToken);
}
+ ///
+ /// Removes the ad-hoc authorizations that are marked as invalid or have no valid token attached.
+ ///
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task PruneInvalidAsync(CancellationToken cancellationToken = default)
+ {
+ ImmutableArray authorizations;
+
+ do
+ {
+ // Note: don't use an offset here, as the elements returned by this method
+ // are progressively removed from the database immediately after calling it.
+ authorizations = await ListInvalidAsync(100, 0, cancellationToken);
+
+ foreach (var authorization in authorizations)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ await DeleteAsync(authorization, cancellationToken);
+
+ Logger.LogDebug("The authorization {AuthorizationId} was successfully removed from the database.",
+ await GetIdAsync(authorization, cancellationToken));
+ }
+
+ catch (Exception exception)
+ {
+ Logger.LogDebug(exception,
+ "An error occurred while removing the authorization {AuthorizationId} from the database.",
+ await GetIdAsync(authorization, cancellationToken));
+ }
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+
+ while (!authorizations.IsDefaultOrEmpty);
+ }
+
///
/// Revokes an authorization.
///
diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
index 36b3ae80..f0a52317 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
@@ -623,6 +623,49 @@ namespace OpenIddict.Core
}
}
+ ///
+ /// Removes the tokens that are marked as expired or invalid.
+ ///
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task PruneInvalidAsync(CancellationToken cancellationToken = default)
+ {
+ ImmutableArray tokens;
+
+ do
+ {
+ // Note: don't use an offset here, as the elements returned by this method
+ // are progressively removed from the database immediately after calling it.
+ tokens = await ListInvalidAsync(100, 0, cancellationToken);
+
+ foreach (var token in tokens)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ await DeleteAsync(token, cancellationToken);
+
+ Logger.LogDebug("The token {TokenId} was successfully removed from the database.",
+ await GetIdAsync(token, cancellationToken));
+ }
+
+ catch (Exception exception)
+ {
+ Logger.LogDebug(exception,
+ "An error occurred while removing the token {TokenId} from the database.",
+ await GetIdAsync(token, cancellationToken));
+ }
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+
+ while (!tokens.IsDefaultOrEmpty);
+ }
+
///
/// Redeems a token.
///