Kugel
Kugel

Reputation: 19814

Why am I getting sporadic invalid tokens for email verification in ASP.NET Identity?

I'm trying to figure out why my users are getting frequent invalid tokens when confirming their email. I can't reproduce the issue.

Here's the setup:

userManager.UserTokenProvider = new EmailTokenProvider<User>();

Here's how the token is generated:

var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);

Here's how it's verified:

var result = await userManager.ConfirmEmailAsync(user.Id, code);

I understand that the EmailTokenProvider uses security stamp for invalidation. My understanding is that this stamp only changes when significant changes to the User entity are made like changing password or username. However I'm getting invalid tokens way more frequently for this to be the only explanation.

I'm looking for any pointer that would help me shed some light into why this is occurring.

Edit:

I dug around the source code (documentation is very poor) and as @trailmax pointed out below EmailTokenProvider is wrong for this use-case. It is based on TotpSecurityStampBasedTokenProvider which has hard-coded timeout on tokens to 3 minutes!

Upvotes: 5

Views: 1605

Answers (1)

trailmax
trailmax

Reputation: 35106

EmailTokenProvider generates very short-lived token and looks like 6 digits. This token is aimed to be a 2FA and only valid for short period of time (something like 10-15 minutes, don't know the actual value).

The best you can do is to use Identity-provided DataProtectorTokenProvider and that's a bit tricky, because it is not simple to rip it out of OWIN's hands.

The way I go around it is assign static variable in my Start.Auth.cs and then reuse it in UserManager:

public class AuthConfig
{
    public static IDataProtectionProvider DataProtectionProvider { get; set; }

    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
    }

    public void ConfigureAuth(IAppBuilder app)
    {
        DataProtectionProvider = app.GetDataProtectionProvider();

        // do other configuration 
    }
}

And in then re-assign it in constructor of UserManager:

        var dataProtectorProvider = AuthConfig.DataProtectionProvider;
        var dataProtector = dataProtectorProvider.Create("My Asp.Net Identity");
        this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, Guid>(dataProtector)
        {
            TokenLifespan = TimeSpan.FromHours(24),
        };

This way you get very long email token that lasts for 24 hours.

I've done the same mistake as you did, but had to correct pretty soon as users complained.

Upvotes: 8

Related Questions