diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider.cs
index 6cbdeece99..47607abe7e 100644
--- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider.cs
+++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider.cs
@@ -8,7 +8,7 @@ using Volo.Abp.Threading;
namespace Volo.Abp.Identity.AspNetCore;
///
-/// Link-user token provider that enforces only the most recently issued
+/// Link-user token provider that enforces, per purpose, only the most recently issued
/// token to be valid, with a configurable expiration period.
///
public class LinkUserTokenProvider : AbpSingleActiveTokenProvider
diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs
index f005b52cca..25a5f299ff 100644
--- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Domain.Services;
using Volo.Abp.MultiTenancy;
+using Volo.Abp.Uow;
namespace Volo.Abp.Identity;
@@ -162,11 +163,16 @@ public class IdentityLinkUserManager : DomainService
}
}
+ [UnitOfWork]
public virtual async Task SetLinkConsentAsync(IdentityLinkUserInfo sourceLinkUser, string consent, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(sourceLinkUser.TenantId))
{
- var user = await UserManager.GetByIdAsync(sourceLinkUser.UserId);
+ var user = await UserManager.FindByIdAsync(sourceLinkUser.UserId.ToString());
+ if (user == null)
+ {
+ return;
+ }
(await UserManager.SetAuthenticationTokenAsync(
user,
LinkUserTokenProviderConsts.LinkUserConsentLoginProvider,
@@ -175,6 +181,7 @@ public class IdentityLinkUserManager : DomainService
}
}
+ [UnitOfWork]
public virtual async Task GetLinkConsentAsync(IdentityLinkUserInfo sourceLinkUser, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(sourceLinkUser.TenantId))
@@ -191,6 +198,7 @@ public class IdentityLinkUserManager : DomainService
}
}
+ [UnitOfWork]
public virtual async Task RemoveLinkConsentAsync(IdentityLinkUserInfo sourceLinkUser, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(sourceLinkUser.TenantId))
diff --git a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider_Tests.cs b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider_Tests.cs
index 962538a5ae..7dfca71f92 100644
--- a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider_Tests.cs
+++ b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider_Tests.cs
@@ -34,7 +34,7 @@ public class LinkUserTokenProvider_Tests : AbpSingleActiveTokenProviderTestBase
=> LinkUserTokenProviderConsts.LinkUserTokenPurpose;
[Fact]
- public void LinkUserTokenProvider_Should_Be_Register()
+ public void LinkUserTokenProvider_Should_Be_Registered()
{
var identityOptions = GetRequiredService>().Value;
@@ -76,4 +76,50 @@ public class LinkUserTokenProvider_Tests : AbpSingleActiveTokenProviderTestBase
await uow.CompleteAsync();
}
}
+
+ [Fact]
+ public async Task RemoveLinkUserTokenAsync_With_Custom_Purpose_Should_Invalidate_Only_That_Purpose()
+ {
+ const string customPurpose = "CustomLinkUserPurpose";
+
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ var john = await UserRepository.GetAsync(TestData.UserJohnId);
+ var defaultPurposeToken = await GenerateTokenAsync(john);
+ var customPurposeToken = await IdentityLinkUserManager.GenerateLinkTokenAsync(
+ new IdentityLinkUserInfo(john.Id, john.TenantId),
+ customPurpose);
+
+ john = await UserRepository.GetAsync(TestData.UserJohnId);
+ (await UserManager.RemoveLinkUserTokenAsync(john, customPurpose)).Succeeded.ShouldBeTrue();
+
+ john = await UserRepository.GetAsync(TestData.UserJohnId);
+ (await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), customPurposeToken, customPurpose)).ShouldBeFalse();
+ (await VerifyTokenAsync(john, defaultPurposeToken)).ShouldBeTrue();
+
+ await uow.CompleteAsync();
+ }
+ }
+
+ [Fact]
+ public async Task GenerateLinkTokenAsync_With_Custom_Purpose_Should_Invalidate_Previous_Token_For_Same_Purpose()
+ {
+ const string customPurpose = "CustomLinkUserPurpose";
+
+ using (var uow = UnitOfWorkManager.Begin())
+ {
+ var john = await UserRepository.GetAsync(TestData.UserJohnId);
+ var firstToken = await IdentityLinkUserManager.GenerateLinkTokenAsync(
+ new IdentityLinkUserInfo(john.Id, john.TenantId),
+ customPurpose);
+ var secondToken = await IdentityLinkUserManager.GenerateLinkTokenAsync(
+ new IdentityLinkUserInfo(john.Id, john.TenantId),
+ customPurpose);
+
+ (await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), firstToken, customPurpose)).ShouldBeFalse();
+ (await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), secondToken, customPurpose)).ShouldBeTrue();
+
+ await uow.CompleteAsync();
+ }
+ }
}