directly save changes for raven session

This commit is contained in:
2019-08-25 21:29:06 +02:00
parent 523fcab5fa
commit 1975a4668a
3 changed files with 151 additions and 74 deletions
+3 -3
View File
@@ -7,7 +7,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>RavenDB.Identity</AssemblyName> <AssemblyName>RavenDB.Identity</AssemblyName>
<Title>RavenDB Identity for ASP.NET Core</Title> <Title>RavenDB Identity for ASP.NET Core</Title>
<PackageId>RavenDB.Identity</PackageId> <PackageId>RavenDB.IdentityVT</PackageId>
<PackageTags>ravendb identity aspnetcore membership authentication nosql authorization</PackageTags> <PackageTags>ravendb identity aspnetcore membership authentication nosql authorization</PackageTags>
<PackageProjectUrl>https://github.com/JudahGabriel/RavenDB.Identity</PackageProjectUrl> <PackageProjectUrl>https://github.com/JudahGabriel/RavenDB.Identity</PackageProjectUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
@@ -19,7 +19,7 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Version>6.1</Version> <Version>16.1</Version>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild> <GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<RootNamespace>Raven.Identity</RootNamespace> <RootNamespace>Raven.Identity</RootNamespace>
<PackageReleaseNotes>Custom IdentityRole types are stored using their type name in the ID. UserStore can now handle custom IdentityRole types.</PackageReleaseNotes> <PackageReleaseNotes>Custom IdentityRole types are stored using their type name in the ID. UserStore can now handle custom IdentityRole types.</PackageReleaseNotes>
@@ -29,7 +29,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="RavenDB.Client" Version="4.2.0" /> <PackageReference Include="RavenDB.Client" Version="4.2.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+15 -7
View File
@@ -230,7 +230,7 @@ namespace Raven.Identity
/// <param name="roleName">The name of the role.</param> /// <param name="roleName">The name of the role.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns> /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken)) public async Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed(); ThrowIfDisposed();
@@ -238,8 +238,11 @@ namespace Raven.Identity
{ {
throw new ArgumentNullException(nameof(role)); throw new ArgumentNullException(nameof(role));
} }
using (IAsyncDocumentSession session = Store.OpenAsyncSession())
{
role.Name = roleName; role.Name = roleName;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <summary> /// <summary>
@@ -359,7 +362,7 @@ namespace Raven.Identity
/// <param name="claim">The claim to add to the role.</param> /// <param name="claim">The claim to add to the role.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns> /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) public async Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (role == null) if (role == null)
@@ -370,9 +373,12 @@ namespace Raven.Identity
{ {
throw new ArgumentNullException(nameof(claim)); throw new ArgumentNullException(nameof(claim));
} }
role.Claims.Add(CreateRoleClaim(role, claim));
return Task.FromResult(false); using (IAsyncDocumentSession session = Store.OpenAsyncSession())
{
role.Claims.Add(CreateRoleClaim(role, claim));
await session.SaveChangesAsync(cancellationToken);
}
} }
/// <summary> /// <summary>
@@ -394,13 +400,15 @@ namespace Raven.Identity
throw new ArgumentNullException(nameof(claim)); throw new ArgumentNullException(nameof(claim));
} }
await Task.FromResult(0); using (IAsyncDocumentSession session = Store.OpenAsyncSession())
{
var claims = role.Claims.Where(c => c.ClaimValue == claim.Value).ToList(); var claims = role.Claims.Where(c => c.ClaimValue == claim.Value).ToList();
foreach (var c in claims) foreach (var c in claims)
{ {
role.Claims.Remove(c); role.Claims.Remove(c);
} }
await session.SaveChangesAsync(cancellationToken);
}
} }
/// <summary> /// <summary>
+113 -44
View File
@@ -79,12 +79,16 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken) public async Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
user.UserName = userName; user.UserName = userName;
return Task.CompletedTask;
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -97,12 +101,16 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken) public async Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
user.UserName = normalizedName.ToLowerInvariant(); user.UserName = normalizedName.ToLowerInvariant();
return Task.CompletedTask;
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -164,7 +172,7 @@ namespace Raven.Identity
// we need to roll back the email reservation. // we need to roll back the email reservation.
try try
{ {
await session.SaveChangesAsync(); await session.SaveChangesAsync(cancellationToken);
} }
catch (Exception) catch (Exception)
{ {
@@ -212,7 +220,7 @@ namespace Raven.Identity
using (IAsyncDocumentSession session = _store.OpenAsyncSession()) using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{ {
session.Delete(user); session.Delete(user);
await session.SaveChangesAsync(); await session.SaveChangesAsync(cancellationToken);
} }
return IdentityResult.Success; return IdentityResult.Success;
@@ -281,6 +289,7 @@ namespace Raven.Identity
using (IAsyncDocumentSession session = _store.OpenAsyncSession()) using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{ {
await session.StoreAsync(userLogin); await session.StoreAsync(userLogin);
await session.SaveChangesAsync(cancellationToken);
} }
} }
} }
@@ -299,6 +308,7 @@ namespace Raven.Identity
if (loginDoc != null) if (loginDoc != null)
{ {
session.Delete(loginDoc); session.Delete(loginDoc);
await session.SaveChangesAsync(cancellationToken);
} }
} }
@@ -349,12 +359,15 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken) public async Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.Claims.AddRange(claims.Select(c => new IdentityUserClaim { ClaimType = c.Type, ClaimValue = c.Value })); user.Claims.AddRange(claims.Select(c => new IdentityUserClaim { ClaimType = c.Type, ClaimValue = c.Value }));
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -366,17 +379,20 @@ namespace Raven.Identity
if (indexOfClaim != -1) if (indexOfClaim != -1)
{ {
user.Claims.RemoveAt(indexOfClaim); user.Claims.RemoveAt(indexOfClaim);
await this.AddClaimsAsync(user, new[] { newClaim }, cancellationToken); await AddClaimsAsync(user, new[] { newClaim }, cancellationToken);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken) public async Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.Claims.RemoveAll(identityClaim => claims.Any(c => c.Type == identityClaim.ClaimType && c.Value == identityClaim.ClaimValue)); user.Claims.RemoveAll(identityClaim => claims.Any(c => c.Type == identityClaim.ClaimType && c.Value == identityClaim.ClaimValue));
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -434,6 +450,8 @@ namespace Raven.Identity
{ {
existingRoleOrNull.Users.Add(user.Id); existingRoleOrNull.Users.Add(user.Id);
} }
await session.SaveChangesAsync(cancellationToken);
} }
} }
@@ -453,6 +471,7 @@ namespace Raven.Identity
{ {
roleOrNull.Users.Remove(user.Id); roleOrNull.Users.Remove(user.Id);
} }
await session.SaveChangesAsync(cancellationToken);
} }
} }
@@ -498,12 +517,15 @@ namespace Raven.Identity
#region IUserPasswordStore implementation #region IUserPasswordStore implementation
/// <inheritdoc /> /// <inheritdoc />
public Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken) public async Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.PasswordHash = passwordHash; user.PasswordHash = passwordHash;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -527,12 +549,15 @@ namespace Raven.Identity
#region IUserSecurityStampStore implementation #region IUserSecurityStampStore implementation
/// <inheritdoc /> /// <inheritdoc />
public Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken) public async Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.SecurityStamp = stamp; user.SecurityStamp = stamp;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -548,11 +573,14 @@ namespace Raven.Identity
#region IUserEmailStore implementation #region IUserEmailStore implementation
/// <inheritdoc /> /// <inheritdoc />
public Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken) public async Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken)
{ {
ThrowIfDisposedOrCancelled(cancellationToken); ThrowIfDisposedOrCancelled(cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.Email = email ?? throw new ArgumentNullException(nameof(email)); user.Email = email ?? throw new ArgumentNullException(nameof(email));
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -572,12 +600,15 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken) public async Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.EmailConfirmed = confirmed; user.EmailConfirmed = confirmed;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -602,7 +633,7 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken) public async Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
if (string.IsNullOrEmpty(normalizedEmail)) if (string.IsNullOrEmpty(normalizedEmail))
@@ -610,8 +641,11 @@ namespace Raven.Identity
throw new ArgumentNullException(nameof(normalizedEmail)); throw new ArgumentNullException(nameof(normalizedEmail));
} }
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.Email = normalizedEmail.ToLowerInvariant(); // I don't like the ALL CAPS default. We're going all lower. user.Email = normalizedEmail.ToLowerInvariant(); // I don't like the ALL CAPS default. We're going all lower.
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
#endregion #endregion
@@ -627,30 +661,41 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken) public async Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.LockoutEnd = lockoutEnd; user.LockoutEnd = lockoutEnd;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<int> IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken) public async Task<int> IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.AccessFailedCount++; user.AccessFailedCount++;
return Task.FromResult(user.AccessFailedCount); await session.SaveChangesAsync(cancellationToken);
}
return user.AccessFailedCount;
} }
/// <inheritdoc /> /// <inheritdoc />
public Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken) public async Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.AccessFailedCount = 0; user.AccessFailedCount = 0;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -669,12 +714,15 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken) public async Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.LockoutEnabled = enabled; user.LockoutEnabled = enabled;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
#endregion #endregion
@@ -682,12 +730,15 @@ namespace Raven.Identity
#region IUserTwoFactorStore implementation #region IUserTwoFactorStore implementation
/// <inheritdoc /> /// <inheritdoc />
public Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken) public async Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.TwoFactorEnabled = enabled; user.TwoFactorEnabled = enabled;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -703,12 +754,15 @@ namespace Raven.Identity
#region IUserPhoneNumberStore implementation #region IUserPhoneNumberStore implementation
/// <inheritdoc /> /// <inheritdoc />
public Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken) public async Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.PhoneNumber = phoneNumber; user.PhoneNumber = phoneNumber;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -728,12 +782,15 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken) public async Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken)
{ {
ThrowIfNullDisposedCancelled(user, cancellationToken); ThrowIfNullDisposedCancelled(user, cancellationToken);
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
user.PhoneNumberConfirmed = confirmed; user.PhoneNumberConfirmed = confirmed;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
#endregion #endregion
@@ -741,10 +798,13 @@ namespace Raven.Identity
#region IUserAuthenticatorKeyStore implementation #region IUserAuthenticatorKeyStore implementation
/// <inheritdoc /> /// <inheritdoc />
public Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken) public async Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken)
{
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{ {
user.TwoFactorAuthenticatorKey = key; user.TwoFactorAuthenticatorKey = key;
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -780,20 +840,21 @@ namespace Raven.Identity
} }
existingOrNull.Value = value; existingOrNull.Value = value;
await session.SaveChangesAsync(cancellationToken);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) public async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
{ {
var id = IdentityUserAuthToken.GetWellKnownId(_store, user.Id, loginProvider, name); var id = IdentityUserAuthToken.GetWellKnownId(_store, user.Id, loginProvider, name);
using (IAsyncDocumentSession session = _store.OpenAsyncSession()) using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{ {
session.Delete(id); session.Delete(id);
await session.SaveChangesAsync(cancellationToken);
} }
return Task.CompletedTask;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -814,16 +875,24 @@ namespace Raven.Identity
} }
/// <inheritdoc /> /// <inheritdoc />
public Task ReplaceCodesAsync(TUser user, IEnumerable<string> recoveryCodes, CancellationToken cancellationToken) public async Task ReplaceCodesAsync(TUser user, IEnumerable<string> recoveryCodes, CancellationToken cancellationToken)
{
using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{ {
user.TwoFactorRecoveryCodes = new List<string>(recoveryCodes); user.TwoFactorRecoveryCodes = new List<string>(recoveryCodes);
return Task.CompletedTask; await session.SaveChangesAsync(cancellationToken);
}
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<bool> RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken) public async Task<bool> RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken)
{ {
return Task.FromResult(user.TwoFactorRecoveryCodes.Remove(code)); using (IAsyncDocumentSession session = _store.OpenAsyncSession())
{
bool isRemoved = user.TwoFactorRecoveryCodes.Remove(code);
await session.SaveChangesAsync(cancellationToken);
return isRemoved;
}
} }
/// <inheritdoc /> /// <inheritdoc />