.Net Core 身份电子邮件确认令牌一旦使用就应该无效

.Net Core Identtiy Email Confirm Token should be invalid once already used

我正在使用 GenerateEmailConfirmationTokenAsync to generate the token Email Confirmation Link and using ConfirmEmailAsync 来验证 link。它工作正常。

问题 - Link 应该只能工作一次。如果用户第二次使用相同的 link link 应该是无效的。我调试并发现每次 ConfirmEmailAsync IdentityResult.IsSucceded true. I was expecting VerifyUserTokenAsync 第二次应该 return false 但它总是 returning true.

请提出解决方案。谢谢

使用 .Net Core 3.1,Identity Server 4

// To Generate Token
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);

// To Confirm Token
var result = await _userManager.ConfirmEmailAsync(user, code);

// Customised Token Provider
public class EmailConfirmationTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class
{
    public EmailConfirmationTokenProvider(IDataProtectionProvider dataProtectionProvider, 
        IOptions<EmailConfirmationTokenProviderOptions> options, ILogger<EmailConfirmationTokenProvider<TUser>> logger) 
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class EmailConfirmationTokenProviderOptions : DataProtectionTokenProviderOptions
{ }

// Starup.cs Code
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.Tokens.EmailConfirmationTokenProvider = "email_confirmation_provider";
    options.SignIn.RequireConfirmedEmail = true;
})
    .AddDefaultTokenProviders()
    .AddTokenProvider<EmailConfirmationTokenProvider<ApplicationUser>>("email_confirmation_provider");

services.Configure<EmailConfirmationTokenProviderOptions>(options =>
{
     options.TokenLifespan = TimeSpan.FromSeconds(3600);
});

我认为您需要将 ConfirmEmailAsync 的行为改写成这样,

如果令牌与已知用户匹配,则表明它是有效颁发的令牌。 然后将尝试与用户管理器确认令牌。 如果确认失败,则令牌已过期并采取适当的措施。

否则,如果令牌已确认,则会将其从关联用户中删除,从而使该令牌的重用无效。

public override async System.Threading.Tasks.Task<IdentityResult> ConfirmEmailAsync(string userId, string token) {
var user = await FindByIdAsync(userId);
if (user == null) {
    return IdentityResult.Failed("User Id Not Found");
}
var result = await base.ConfirmEmailAsync(userId, token);
if (result.Succeeded) {
    user.EmailConfirmationToken = null;
    return await UpdateAsync(user);
} else if (user.EmailConfirmationToken == token) {
    //Previously Issued Token expired
    result = IdentityResult.Failed("Expired Token");
}
return result;

}

密码重置也可以执行类似的操作。

方法二,还没试过试一试, 尝试修改令牌的安全时间戳以在确认完成后使它们失效,

UserManager.UpdateSecurityStampAsync(userId);