diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
index 6b9e61c1..7a9e41b2 100644
--- a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
@@ -394,6 +394,37 @@ public interface IOpenIddictAuthorizationManager
/// The number of authorizations that were removed.
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default);
+ ///
+ /// Revokes all 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.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken = default);
+
+ ///
+ /// Revokes all 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.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken = default);
+
+ ///
+ /// Revokes all 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 that can be used to abort the operation.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken = default);
+
///
/// Revokes all the authorizations associated with the specified application identifier.
///
diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs
index f8e4c0ba..3ddcfde7 100644
--- a/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs
+++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs
@@ -409,6 +409,37 @@ public interface IOpenIddictTokenManager
/// The number of tokens that were removed.
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default);
+ ///
+ /// Revokes all the tokens corresponding to the specified
+ /// subject and associated with the application identifier.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken = default);
+
+ ///
+ /// Revokes all the tokens matching the specified parameters.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The token status.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken = default);
+
+ ///
+ /// Revokes all the tokens matching the specified parameters.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The token status.
+ /// The token type.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken = default);
+
///
/// Revokes all the tokens associated with the specified application identifier.
///
diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs
index 01121dac..cac2c297 100644
--- a/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs
@@ -279,6 +279,37 @@ public interface IOpenIddictAuthorizationStore where TAuthorizat
/// The number of authorizations that were removed.
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken);
+ ///
+ /// Revokes all 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.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken);
+
+ ///
+ /// Revokes all 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.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken);
+
+ ///
+ /// Revokes all 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 that can be used to abort the operation.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken);
+
///
/// Revokes all the authorizations associated with the specified application identifier.
///
diff --git a/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs
index af65b61f..b2cde75d 100644
--- a/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs
+++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs
@@ -326,6 +326,37 @@ public interface IOpenIddictTokenStore where TToken : class
/// The number of tokens that were removed.
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken);
+ ///
+ /// Revokes all the tokens corresponding to the specified
+ /// subject and associated with the application identifier.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken);
+
+ ///
+ /// Revokes all the tokens matching the specified parameters.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The token status.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken);
+
+ ///
+ /// Revokes all the tokens matching the specified parameters.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The token status.
+ /// The token type.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken);
+
///
/// Revokes all the tokens associated with the specified application identifier.
///
diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index 54f3aad9..e2c4b556 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -1028,6 +1028,91 @@ public class OpenIddictAuthorizationManager : IOpenIddictAuthori
public virtual ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default)
=> Store.PruneAsync(threshold, cancellationToken);
+ ///
+ /// Revokes all 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.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ public virtual ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ return Store.RevokeAsync(subject, client, cancellationToken);
+ }
+
+ ///
+ /// Revokes all 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.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ public virtual ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ return Store.RevokeAsync(subject, client, status, cancellationToken);
+ }
+
+ ///
+ /// Revokes all 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 that can be used to abort the operation.
+ /// The number of authorizations corresponding to the criteria that were marked as revoked.
+ public virtual ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ return Store.RevokeAsync(subject, client, status, type, cancellationToken);
+ }
+
///
/// Revokes all the authorizations associated with the specified application identifier.
///
@@ -1369,6 +1454,18 @@ public class OpenIddictAuthorizationManager : IOpenIddictAuthori
ValueTask IOpenIddictAuthorizationManager.PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
=> PruneAsync(threshold, cancellationToken);
+ ///
+ ValueTask IOpenIddictAuthorizationManager.RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ => RevokeAsync(subject, client, cancellationToken);
+
+ ///
+ ValueTask IOpenIddictAuthorizationManager.RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ => RevokeAsync(subject, client, status, cancellationToken);
+
+ ///
+ ValueTask IOpenIddictAuthorizationManager.RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ => RevokeAsync(subject, client, status, type, cancellationToken);
+
///
ValueTask IOpenIddictAuthorizationManager.RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
=> RevokeByApplicationIdAsync(identifier, cancellationToken);
diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
index d7438298..c16ba726 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
@@ -1055,6 +1055,91 @@ public class OpenIddictTokenManager : IOpenIddictTokenManager where TTok
public virtual ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default)
=> Store.PruneAsync(threshold, cancellationToken);
+ ///
+ /// Revokes all the tokens corresponding to the specified
+ /// subject and associated with the application identifier.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ public virtual ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ return Store.RevokeAsync(subject, client, cancellationToken);
+ }
+
+ ///
+ /// Revokes all the tokens matching the specified parameters.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The token status.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ public virtual ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ return Store.RevokeAsync(subject, client, status, cancellationToken);
+ }
+
+ ///
+ /// Revokes all the tokens matching the specified parameters.
+ ///
+ /// The subject associated with the token.
+ /// The client associated with the token.
+ /// The token status.
+ /// The token type.
+ /// The that can be used to abort the operation.
+ /// The number of tokens corresponding to the criteria that were marked as revoked.
+ public virtual ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ return Store.RevokeAsync(subject, client, status, type, cancellationToken);
+ }
+
///
/// Revokes all the tokens associated with the specified application identifier.
///
@@ -1533,6 +1618,18 @@ public class OpenIddictTokenManager : IOpenIddictTokenManager where TTok
ValueTask IOpenIddictTokenManager.PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
=> PruneAsync(threshold, cancellationToken);
+ ///
+ ValueTask IOpenIddictTokenManager.RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ => RevokeAsync(subject, client, cancellationToken);
+
+ ///
+ ValueTask IOpenIddictTokenManager.RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ => RevokeAsync(subject, client, status, cancellationToken);
+
+ ///
+ ValueTask IOpenIddictTokenManager.RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ => RevokeAsync(subject, client, status, type, cancellationToken);
+
///
ValueTask IOpenIddictTokenManager.RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
=> RevokeByApplicationIdAsync(identifier, cancellationToken);
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs
index d6020f77..b0ba2e06 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkAuthorizationStore.cs
@@ -658,6 +658,182 @@ public class OpenIddictEntityFrameworkAuthorizationStore
+ public virtual async ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+ List? exceptions = null;
+
+ var result = 0L;
+
+ foreach (var authorization in await (from authorization in Authorizations
+ where authorization.Application!.Id!.Equals(key) && authorization.Subject == subject
+ select authorization).ToListAsync(cancellationToken))
+ {
+ authorization.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(authorization).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+ List? exceptions = null;
+
+ var result = 0L;
+
+ foreach (var authorization in await (from authorization in Authorizations
+ where authorization.Application!.Id!.Equals(key) &&
+ authorization.Subject == subject &&
+ authorization.Status == status
+ select authorization).ToListAsync(cancellationToken))
+ {
+ authorization.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(authorization).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+ List? exceptions = null;
+
+ var result = 0L;
+
+ foreach (var authorization in await (from authorization in Authorizations
+ where authorization.Application!.Id!.Equals(key) &&
+ authorization.Subject == subject &&
+ authorization.Status == status &&
+ authorization.Type == type
+ select authorization).ToListAsync(cancellationToken))
+ {
+ authorization.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(authorization).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
///
public virtual async ValueTask RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{
diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs
index d790bf44..56edf7bb 100644
--- a/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs
+++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictEntityFrameworkTokenStore.cs
@@ -659,6 +659,182 @@ public class OpenIddictEntityFrameworkTokenStore
+ public virtual async ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+ List? exceptions = null;
+
+ var result = 0L;
+
+ foreach (var token in await (from token in Tokens
+ where token.Application!.Id!.Equals(key) && token.Subject == subject
+ select token).ToListAsync(cancellationToken))
+ {
+ token.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(token).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+ List? exceptions = null;
+
+ var result = 0L;
+
+ foreach (var token in await (from token in Tokens
+ where token.Application!.Id!.Equals(key) &&
+ token.Subject == subject &&
+ token.Status == status
+ select token).ToListAsync(cancellationToken))
+ {
+ token.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(token).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+ List? exceptions = null;
+
+ var result = 0L;
+
+ foreach (var token in await (from token in Tokens
+ where token.Application!.Id!.Equals(key) &&
+ token.Subject == subject &&
+ token.Status == status &&
+ token.Type == type
+ select token).ToListAsync(cancellationToken))
+ {
+ token.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(token).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
///
public virtual async ValueTask RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs
index 21115ab0..71bb8d54 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictEntityFrameworkCoreAuthorizationStore.cs
@@ -254,9 +254,10 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore ExecuteAsync([EnumeratorCancellation] 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 overridden to use an explicit join before applying the equality check.
+ // 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 query uses use an explicit join to apply the equality check.
+ //
// See https://github.com/openiddict/openiddict-core/issues/499 for more information.
var key = ConvertIdentifierFromString(client);
@@ -409,9 +413,10 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore
+ public virtual async ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+#if SUPPORTS_BULK_DBSET_OPERATIONS
+ if (!Options.CurrentValue.DisableBulkOperations)
+ {
+ return await (
+ from authorization in Authorizations
+ where authorization.Subject == subject && authorization.Application!.Id!.Equals(key)
+ select authorization).ExecuteUpdateAsync(entity => entity.SetProperty(
+ authorization => authorization.Status, Statuses.Revoked), cancellationToken);
+
+ // Note: calling DbContext.SaveChangesAsync() is not necessary
+ // with bulk update operations as they are executed immediately.
+ }
+#endif
+ List? exceptions = null;
+
+ var result = 0L;
+
+ // 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 query uses use an explicit join to apply the equality check.
+ //
+ // See https://github.com/openiddict/openiddict-core/issues/499 for more information.
+
+ foreach (var authorization in await (from authorization in Authorizations.AsTracking()
+ where authorization.Subject == subject
+ join application in Applications.AsTracking() on authorization.Application!.Id equals application.Id
+ where application.Id!.Equals(key)
+ select authorization).ToListAsync(cancellationToken))
+ {
+ authorization.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(authorization).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+#if SUPPORTS_BULK_DBSET_OPERATIONS
+ if (!Options.CurrentValue.DisableBulkOperations)
+ {
+ return await (
+ from authorization in Authorizations
+ where authorization.Subject == subject &&
+ authorization.Status == status &&
+ authorization.Application!.Id!.Equals(key)
+ select authorization).ExecuteUpdateAsync(entity => entity.SetProperty(
+ authorization => authorization.Status, Statuses.Revoked), cancellationToken);
+
+ // Note: calling DbContext.SaveChangesAsync() is not necessary
+ // with bulk update operations as they are executed immediately.
+ }
+#endif
+ List? exceptions = null;
+
+ var result = 0L;
+
+ // 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 query uses use an explicit join to apply the equality check.
+ //
+ // See https://github.com/openiddict/openiddict-core/issues/499 for more information.
+
+ foreach (var authorization in await (from authorization in Authorizations.AsTracking()
+ where authorization.Subject == subject && authorization.Status == status
+ join application in Applications.AsTracking() on authorization.Application!.Id equals application.Id
+ where application.Id!.Equals(key)
+ select authorization).ToListAsync(cancellationToken))
+ {
+ authorization.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(authorization).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+#if SUPPORTS_BULK_DBSET_OPERATIONS
+ if (!Options.CurrentValue.DisableBulkOperations)
+ {
+ return await (
+ from authorization in Authorizations
+ where authorization.Subject == subject &&
+ authorization.Status == status &&
+ authorization.Type == type &&
+ authorization.Application!.Id!.Equals(key)
+ select authorization).ExecuteUpdateAsync(entity => entity.SetProperty(
+ authorization => authorization.Status, Statuses.Revoked), cancellationToken);
+
+ // Note: calling DbContext.SaveChangesAsync() is not necessary
+ // with bulk update operations as they are executed immediately.
+ }
+#endif
+ List? exceptions = null;
+
+ var result = 0L;
+
+ // 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 query uses use an explicit join to apply the equality check.
+ //
+ // See https://github.com/openiddict/openiddict-core/issues/499 for more information.
+
+ foreach (var authorization in await (from authorization in Authorizations.AsTracking()
+ where authorization.Subject == subject && authorization.Status == status && authorization.Type == type
+ join application in Applications.AsTracking() on authorization.Application!.Id equals application.Id
+ where application.Id!.Equals(key)
+ select authorization).ToListAsync(cancellationToken))
+ {
+ authorization.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(authorization).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
///
public virtual async ValueTask RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{
@@ -826,8 +1070,15 @@ public class OpenIddictEntityFrameworkCoreAuthorizationStore
+ public virtual async ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+#if SUPPORTS_BULK_DBSET_OPERATIONS
+ if (!Options.CurrentValue.DisableBulkOperations)
+ {
+ return await (
+ from token in Tokens
+ where token.Subject == subject && token.Application!.Id!.Equals(key)
+ select token).ExecuteUpdateAsync(entity => entity.SetProperty(
+ token => token.Status, Statuses.Revoked), cancellationToken);
+
+ // Note: calling DbContext.SaveChangesAsync() is not necessary
+ // with bulk update operations as they are executed immediately.
+ }
+#endif
+ List? exceptions = null;
+
+ var result = 0L;
+
+ // 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 query uses use an explicit join to apply the equality check.
+ //
+ // See https://github.com/openiddict/openiddict-core/issues/499 for more information.
+
+ foreach (var token in await (from token in Tokens.AsTracking()
+ where token.Subject == subject
+ join application in Applications.AsTracking() on token.Application!.Id equals application.Id
+ where application.Id!.Equals(key)
+ select token).ToListAsync(cancellationToken))
+ {
+ token.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(token).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+#if SUPPORTS_BULK_DBSET_OPERATIONS
+ if (!Options.CurrentValue.DisableBulkOperations)
+ {
+ return await (
+ from token in Tokens
+ where token.Subject == subject && token.Status == status && token.Application!.Id!.Equals(key)
+ select token).ExecuteUpdateAsync(entity => entity.SetProperty(
+ token => token.Status, Statuses.Revoked), cancellationToken);
+
+ // Note: calling DbContext.SaveChangesAsync() is not necessary
+ // with bulk update operations as they are executed immediately.
+ }
+#endif
+ List? exceptions = null;
+
+ var result = 0L;
+
+ // 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 query uses use an explicit join to apply the equality check.
+ //
+ // See https://github.com/openiddict/openiddict-core/issues/499 for more information.
+
+ foreach (var token in await (from token in Tokens.AsTracking()
+ where token.Subject == subject && token.Status == status
+ join application in Applications.AsTracking() on token.Application!.Id equals application.Id
+ where application.Id!.Equals(key)
+ select token).ToListAsync(cancellationToken))
+ {
+ token.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(token).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ var key = ConvertIdentifierFromString(client);
+
+#if SUPPORTS_BULK_DBSET_OPERATIONS
+ if (!Options.CurrentValue.DisableBulkOperations)
+ {
+ return await (
+ from token in Tokens
+ where token.Subject == subject &&
+ token.Status == status &&
+ token.Type == type &&
+ token.Application!.Id!.Equals(key)
+ select token).ExecuteUpdateAsync(entity => entity.SetProperty(
+ token => token.Status, Statuses.Revoked), cancellationToken);
+
+ // Note: calling DbContext.SaveChangesAsync() is not necessary
+ // with bulk update operations as they are executed immediately.
+ }
+#endif
+ List? exceptions = null;
+
+ var result = 0L;
+
+ // 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 query uses use an explicit join to apply the equality check.
+ //
+ // See https://github.com/openiddict/openiddict-core/issues/499 for more information.
+
+ foreach (var token in await (from token in Tokens.AsTracking()
+ where token.Subject == subject && token.Status == status && token.Type == type
+ join application in Applications.AsTracking() on token.Application!.Id equals application.Id
+ where application.Id!.Equals(key)
+ select token).ToListAsync(cancellationToken))
+ {
+ token.Status = Statuses.Revoked;
+
+ try
+ {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception))
+ {
+ // Reset the state of the entity to prevents future calls to SaveChangesAsync() from failing.
+ Context.Entry(token).State = EntityState.Unchanged;
+
+ exceptions ??= [];
+ exceptions.Add(exception);
+
+ continue;
+ }
+
+ result++;
+ }
+
+ if (exceptions is not null)
+ {
+ throw new AggregateException(SR.GetResourceString(SR.ID0249), exceptions);
+ }
+
+ return result;
+ }
+
///
public virtual async ValueTask RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{
@@ -775,8 +1017,15 @@ public class OpenIddictEntityFrameworkCoreTokenStore : IOpenIddictAu
var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
await foreach (var authorization in collection.Find(authorization =>
- authorization.Subject == subject &&
- authorization.ApplicationId == ObjectId.Parse(client)).ToAsyncEnumerable(cancellationToken))
+ authorization.ApplicationId == ObjectId.Parse(client) &&
+ authorization.Subject == subject).ToAsyncEnumerable(cancellationToken))
{
yield return authorization;
}
@@ -160,8 +160,8 @@ public class OpenIddictMongoDbAuthorizationStore : IOpenIddictAu
var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
await foreach (var authorization in collection.Find(authorization =>
- authorization.Subject == subject &&
authorization.ApplicationId == ObjectId.Parse(client) &&
+ authorization.Subject == subject &&
authorization.Status == status).ToAsyncEnumerable(cancellationToken))
{
yield return authorization;
@@ -202,8 +202,8 @@ public class OpenIddictMongoDbAuthorizationStore : IOpenIddictAu
var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
await foreach (var authorization in collection.Find(authorization =>
- authorization.Subject == subject &&
authorization.ApplicationId == ObjectId.Parse(client) &&
+ authorization.Subject == subject &&
authorization.Status == status &&
authorization.Type == type).ToAsyncEnumerable(cancellationToken))
{
@@ -248,8 +248,8 @@ public class OpenIddictMongoDbAuthorizationStore : IOpenIddictAu
// Note: Enumerable.All() is deliberately used without the extension method syntax to ensure
// ImmutableArrayExtensions.All() (which is not supported by MongoDB) is not used instead.
await foreach (var authorization in collection.Find(authorization =>
- authorization.Subject == subject &&
authorization.ApplicationId == ObjectId.Parse(client) &&
+ authorization.Subject == subject &&
authorization.Status == status &&
authorization.Type == type &&
Enumerable.All(scopes, scope => authorization.Scopes!.Contains(scope))).ToAsyncEnumerable(cancellationToken))
@@ -549,6 +549,95 @@ public class OpenIddictMongoDbAuthorizationStore : IOpenIddictAu
return result;
}
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ var database = await Context.GetDatabaseAsync(cancellationToken);
+ var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
+
+ return (await collection.UpdateManyAsync(
+ filter : authorization => authorization.Subject == subject && authorization.ApplicationId == ObjectId.Parse(client),
+ update : Builders.Update.Set(authorization => authorization.Status, Statuses.Revoked),
+ options : null,
+ cancellationToken: cancellationToken)).MatchedCount;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ var database = await Context.GetDatabaseAsync(cancellationToken);
+ var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
+
+ return (await collection.UpdateManyAsync(
+ filter : authorization => authorization.ApplicationId == ObjectId.Parse(client) &&
+ authorization.Subject == subject &&
+ authorization.Status == status,
+ update : Builders.Update.Set(authorization => authorization.Status, Statuses.Revoked),
+ options : null,
+ cancellationToken: cancellationToken)).MatchedCount;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ var database = await Context.GetDatabaseAsync(cancellationToken);
+ var collection = database.GetCollection(Options.CurrentValue.AuthorizationsCollectionName);
+
+ return (await collection.UpdateManyAsync(
+ filter : authorization => authorization.ApplicationId == ObjectId.Parse(client) &&
+ authorization.Subject == subject &&
+ authorization.Status == status &&
+ authorization.Type == type,
+ update : Builders.Update.Set(authorization => authorization.Status, Statuses.Revoked),
+ options : null,
+ cancellationToken: cancellationToken)).MatchedCount;
+ }
+
///
public virtual async ValueTask RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{
diff --git a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs
index 9396d42c..f14bd5ac 100644
--- a/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs
+++ b/src/OpenIddict.MongoDb/Stores/OpenIddictMongoDbTokenStore.cs
@@ -586,6 +586,95 @@ public class OpenIddictMongoDbTokenStore : IOpenIddictTokenStore
return result;
}
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ var database = await Context.GetDatabaseAsync(cancellationToken);
+ var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName);
+
+ return (await collection.UpdateManyAsync(
+ filter : token => token.ApplicationId == ObjectId.Parse(client) && token.Subject == subject,
+ update : Builders.Update.Set(token => token.Status, Statuses.Revoked),
+ options : null,
+ cancellationToken: cancellationToken)).MatchedCount;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ var database = await Context.GetDatabaseAsync(cancellationToken);
+ var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName);
+
+ return (await collection.UpdateManyAsync(
+ filter : token => token.ApplicationId == ObjectId.Parse(client) &&
+ token.Subject == subject &&
+ token.Status == status,
+ update : Builders.Update.Set(token => token.Status, Statuses.Revoked),
+ options : null,
+ cancellationToken: cancellationToken)).MatchedCount;
+ }
+
+ ///
+ public virtual async ValueTask RevokeAsync(string subject, string client, string status, string type, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(subject))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
+ }
+
+ if (string.IsNullOrEmpty(client))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
+ }
+
+ if (string.IsNullOrEmpty(status))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
+ }
+
+ if (string.IsNullOrEmpty(type))
+ {
+ throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
+ }
+
+ var database = await Context.GetDatabaseAsync(cancellationToken);
+ var collection = database.GetCollection(Options.CurrentValue.TokensCollectionName);
+
+ return (await collection.UpdateManyAsync(
+ filter : token => token.ApplicationId == ObjectId.Parse(client) &&
+ token.Subject == subject &&
+ token.Status == status &&
+ token.Type == type,
+ update : Builders.Update.Set(token => token.Status, Statuses.Revoked),
+ options : null,
+ cancellationToken: cancellationToken)).MatchedCount;
+ }
+
///
public virtual async ValueTask RevokeByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{