TransWikia.com

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

Stack Overflow Asked on November 20, 2021

I am using GenerateEmailConfirmationTokenAsync to generate the token Email Confirmation Link and using ConfirmEmailAsync to Validate the link. It is working fine.

Problem – Link should work only once. If user using same link 2nd time link should be invalid. I debug and found each time ConfirmEmailAsync IdentityResult.IsSucceded true. I was expecting VerifyUserTokenAsync should return false second time but it is always returning true.

Please suggest solution. Thanks

Using .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);
});

One Answer

I think you need to override the behavior of ConfirmEmailAsync to something like this,

If the token matches a known user that indicates that it was a validly issued token. Will then attempt to confirm token with User manager. If confirmation fails then token has expired and an appropriate action is taken.

Else if the token confirmed, it is removed from associated user and thus invalidating the reuse of that token.

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;

}

Similar can be done for password resets.

Approach 2, not tried yet but give a try, Try to modify the Security timestamps of token to invalidate those once confirmation is done,

UserManager.UpdateSecurityStampAsync(userId);

Answered by Sohan on November 20, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP