Browse Source

Refactor token providers to include cancellation token support and update token handling logic

pull/24926/head
maliming 6 months ago
parent
commit
38ef51cec5
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 6
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpChangeEmailTokenProvider.cs
  2. 6
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpEmailConfirmationTokenProvider.cs
  3. 6
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpPasswordResetTokenProvider.cs
  4. 22
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs
  5. 14
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/IdentityUserManagerSingleActiveTokenExtensions.cs
  6. 4
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProviderTestBase.cs
  7. 12
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/IdentityUserManagerSingleActiveTokenExtensions_Tests.cs

6
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpChangeEmailTokenProvider.cs

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Volo.Abp.Identity.AspNetCore;
@ -18,8 +19,9 @@ public class AbpChangeEmailTokenProvider : AbpSingleActiveTokenProvider
IDataProtectionProvider dataProtectionProvider,
IOptions<AbpChangeEmailTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<IdentityUser>> logger,
IIdentityUserRepository userRepository)
: base(dataProtectionProvider, options, logger, userRepository)
IIdentityUserRepository userRepository,
ICancellationTokenProvider cancellationTokenProvider)
: base(dataProtectionProvider, options, logger, userRepository, cancellationTokenProvider)
{
}
}

6
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpEmailConfirmationTokenProvider.cs

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Volo.Abp.Identity.AspNetCore;
@ -31,8 +32,9 @@ public class AbpEmailConfirmationTokenProvider : AbpSingleActiveTokenProvider
IDataProtectionProvider dataProtectionProvider,
IOptions<AbpEmailConfirmationTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<IdentityUser>> logger,
IIdentityUserRepository userRepository)
: base(dataProtectionProvider, options, logger, userRepository)
IIdentityUserRepository userRepository,
ICancellationTokenProvider cancellationTokenProvider)
: base(dataProtectionProvider, options, logger, userRepository, cancellationTokenProvider)
{
}
}

6
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpPasswordResetTokenProvider.cs

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Volo.Abp.Identity.AspNetCore;
@ -18,8 +19,9 @@ public class AbpPasswordResetTokenProvider : AbpSingleActiveTokenProvider
IDataProtectionProvider dataProtectionProvider,
IOptions<AbpPasswordResetTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<IdentityUser>> logger,
IIdentityUserRepository userRepository)
: base(dataProtectionProvider, options, logger, userRepository)
IIdentityUserRepository userRepository,
ICancellationTokenProvider cancellationTokenProvider)
: base(dataProtectionProvider, options, logger, userRepository, cancellationTokenProvider)
{
}
}

22
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProvider.cs

@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Volo.Abp.Identity.AspNetCore;
@ -19,27 +20,36 @@ namespace Volo.Abp.Identity.AspNetCore;
/// </summary>
public abstract class AbpSingleActiveTokenProvider : DataProtectorTokenProvider<IdentityUser>
{
public const string TokenHashSuffix = "_TokenHash";
/// <summary>
/// The internal login provider name used to store token hashes in the user token table.
/// Using a bracketed name clearly distinguishes these internal entries from real external
/// login providers (e.g. Google, GitHub) stored in the same table.
/// </summary>
public const string InternalLoginProvider = "[AbpSingleActiveToken]";
protected IIdentityUserRepository UserRepository { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected AbpSingleActiveTokenProvider(
IDataProtectionProvider dataProtectionProvider,
IOptions<DataProtectionTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<IdentityUser>> logger,
IIdentityUserRepository userRepository)
IIdentityUserRepository userRepository,
ICancellationTokenProvider cancellationTokenProvider)
: base(dataProtectionProvider, options, logger)
{
UserRepository = userRepository;
CancellationTokenProvider = cancellationTokenProvider;
}
public override async Task<string> GenerateAsync(string purpose, UserManager<IdentityUser> manager, IdentityUser user)
{
var token = await base.GenerateAsync(purpose, manager, user);
await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Tokens);
await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Tokens, CancellationTokenProvider.Token);
var tokenHash = ComputeSha256Hash(token);
user.SetToken(Options.Name, purpose + TokenHashSuffix, tokenHash);
user.SetToken(InternalLoginProvider, Options.Name + ":" + purpose, tokenHash);
await manager.UpdateAsync(user);
@ -53,9 +63,9 @@ public abstract class AbpSingleActiveTokenProvider : DataProtectorTokenProvider<
return false;
}
await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Tokens);
await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Tokens, CancellationTokenProvider.Token);
var storedHash = user.FindToken(Options.Name, purpose + TokenHashSuffix)?.Value;
var storedHash = user.FindToken(InternalLoginProvider, Options.Name + ":" + purpose)?.Value;
if (storedHash == null)
{
return false;

14
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/IdentityUserManagerSingleActiveTokenExtensions.cs

@ -7,7 +7,7 @@ namespace Volo.Abp.Identity.AspNetCore;
/// Provides extension methods on <see cref="IdentityUserManager"/> for invalidating
/// single-active tokens managed by <see cref="AbpSingleActiveTokenProvider"/>.
/// These helpers live in the AspNetCore layer because they depend on
/// <see cref="AbpSingleActiveTokenProvider.TokenHashSuffix"/>.
/// <see cref="AbpSingleActiveTokenProvider.InternalLoginProvider"/>.
/// </summary>
public static class IdentityUserManagerSingleActiveTokenExtensions
{
@ -17,8 +17,8 @@ public static class IdentityUserManagerSingleActiveTokenExtensions
/// </summary>
public static Task<IdentityResult> RemovePasswordResetTokenAsync(this IdentityUserManager manager, IdentityUser user)
{
var name = UserManager<IdentityUser>.ResetPasswordTokenPurpose + AbpSingleActiveTokenProvider.TokenHashSuffix;
return manager.RemoveAuthenticationTokenAsync(user, manager.Options.Tokens.PasswordResetTokenProvider, name);
var name = manager.Options.Tokens.PasswordResetTokenProvider + ":" + UserManager<IdentityUser>.ResetPasswordTokenPurpose;
return manager.RemoveAuthenticationTokenAsync(user, AbpSingleActiveTokenProvider.InternalLoginProvider, name);
}
/// <summary>
@ -27,8 +27,8 @@ public static class IdentityUserManagerSingleActiveTokenExtensions
/// </summary>
public static Task<IdentityResult> RemoveEmailConfirmationTokenAsync(this IdentityUserManager manager, IdentityUser user)
{
var name = UserManager<IdentityUser>.ConfirmEmailTokenPurpose + AbpSingleActiveTokenProvider.TokenHashSuffix;
return manager.RemoveAuthenticationTokenAsync(user, manager.Options.Tokens.EmailConfirmationTokenProvider, name);
var name = manager.Options.Tokens.EmailConfirmationTokenProvider + ":" + UserManager<IdentityUser>.ConfirmEmailTokenPurpose;
return manager.RemoveAuthenticationTokenAsync(user, AbpSingleActiveTokenProvider.InternalLoginProvider, name);
}
/// <summary>
@ -37,7 +37,7 @@ public static class IdentityUserManagerSingleActiveTokenExtensions
/// </summary>
public static Task<IdentityResult> RemoveChangeEmailTokenAsync(this IdentityUserManager manager, IdentityUser user, string newEmail)
{
var name = UserManager<IdentityUser>.GetChangeEmailTokenPurpose(newEmail) + AbpSingleActiveTokenProvider.TokenHashSuffix;
return manager.RemoveAuthenticationTokenAsync(user, manager.Options.Tokens.ChangeEmailTokenProvider, name);
var name = manager.Options.Tokens.ChangeEmailTokenProvider + ":" + UserManager<IdentityUser>.GetChangeEmailTokenPurpose(newEmail);
return manager.RemoveAuthenticationTokenAsync(user, AbpSingleActiveTokenProvider.InternalLoginProvider, name);
}
}

4
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSingleActiveTokenProviderTestBase.cs

@ -39,7 +39,7 @@ public abstract class AbpSingleActiveTokenProviderTestBase : AbpIdentityAspNetCo
/// <summary>Returns the token purpose used as the hash key prefix.</summary>
protected abstract string GetPurpose();
private string GetHashKey() => GetPurpose() + AbpSingleActiveTokenProvider.TokenHashSuffix;
private string GetTokenHashName() => GetProviderName() + ":" + GetPurpose();
[Fact]
public async Task Generate_And_Verify_Token_Should_Succeed()
@ -103,7 +103,7 @@ public abstract class AbpSingleActiveTokenProviderTestBase : AbpIdentityAspNetCo
// Overwrite with a non-hex string to simulate data corruption.
user = await UserRepository.GetAsync(TestData.UserJohnId);
await UserManager.SetAuthenticationTokenAsync(user, GetProviderName(), GetHashKey(), "not-valid-hex!!!");
await UserManager.SetAuthenticationTokenAsync(user, AbpSingleActiveTokenProvider.InternalLoginProvider, GetTokenHashName(), "not-valid-hex!!!");
user = await UserRepository.GetAsync(TestData.UserJohnId);

12
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/IdentityUserManagerSingleActiveTokenExtensions_Tests.cs

@ -30,8 +30,8 @@ public class IdentityUserManagerSingleActiveTokenExtensions_Tests : AbpIdentityA
using (var uow = UnitOfWorkManager.Begin())
{
var user = await UserRepository.GetAsync(TestData.UserJohnId);
var providerName = UserManager.Options.Tokens.PasswordResetTokenProvider;
var tokenKey = UserManager<IdentityUser>.ResetPasswordTokenPurpose + AbpSingleActiveTokenProvider.TokenHashSuffix;
var providerName = AbpSingleActiveTokenProvider.InternalLoginProvider;
var tokenKey = UserManager.Options.Tokens.PasswordResetTokenProvider + ":" + UserManager<IdentityUser>.ResetPasswordTokenPurpose;
await UserManager.SetAuthenticationTokenAsync(user, providerName, tokenKey, "hash-value");
(await UserManager.GetAuthenticationTokenAsync(user, providerName, tokenKey)).ShouldNotBeNull();
@ -51,8 +51,8 @@ public class IdentityUserManagerSingleActiveTokenExtensions_Tests : AbpIdentityA
using (var uow = UnitOfWorkManager.Begin())
{
var user = await UserRepository.GetAsync(TestData.UserJohnId);
var providerName = UserManager.Options.Tokens.EmailConfirmationTokenProvider;
var tokenKey = UserManager<IdentityUser>.ConfirmEmailTokenPurpose + AbpSingleActiveTokenProvider.TokenHashSuffix;
var providerName = AbpSingleActiveTokenProvider.InternalLoginProvider;
var tokenKey = UserManager.Options.Tokens.EmailConfirmationTokenProvider + ":" + UserManager<IdentityUser>.ConfirmEmailTokenPurpose;
await UserManager.SetAuthenticationTokenAsync(user, providerName, tokenKey, "hash-value");
(await UserManager.GetAuthenticationTokenAsync(user, providerName, tokenKey)).ShouldNotBeNull();
@ -72,8 +72,8 @@ public class IdentityUserManagerSingleActiveTokenExtensions_Tests : AbpIdentityA
using (var uow = UnitOfWorkManager.Begin())
{
var user = await UserRepository.GetAsync(TestData.UserJohnId);
var providerName = UserManager.Options.Tokens.ChangeEmailTokenProvider;
var tokenKey = UserManager<IdentityUser>.GetChangeEmailTokenPurpose(NewEmail) + AbpSingleActiveTokenProvider.TokenHashSuffix;
var providerName = AbpSingleActiveTokenProvider.InternalLoginProvider;
var tokenKey = UserManager.Options.Tokens.ChangeEmailTokenProvider + ":" + UserManager<IdentityUser>.GetChangeEmailTokenPurpose(NewEmail);
await UserManager.SetAuthenticationTokenAsync(user, providerName, tokenKey, "hash-value");
(await UserManager.GetAuthenticationTokenAsync(user, providerName, tokenKey)).ShouldNotBeNull();

Loading…
Cancel
Save