2

I am trying to verify the email confirmation token for user but always getting INVALID TOKEN error no matter what I do.

My code is very simple

To Generate the token

EmailVerificationCode = await userManager.GenerateEmailConfirmationTokenAsync(user);
EmailVerificationHTMLFormatCode = HttpUtility.UrlEncode(EmailVerificationCode);

To Verify the Token

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

I always get INVALID TOKEN error.

The things I have tried

  1. Checking both generated token and received token for verification by putting them into the database, they are exactly the same.
  2. Tried using HttpUtility.UrlDecode(Code) to decode the token at receiving end
  3. Tried to just use RAW token without HttpUtility.UrlEncode to verify it

I also went through the following solutions

  1. Asp.net 2.0 Identity, ConfirmEmailAsync() getting Invalid Token
  2. Invalid Token. while verifying email verification code using UserManager.ConfirmEmailAsync(user.Id, code)
  3. AspNet.Identit 2.1.0 ConfirmEmailAsync always returns Invalid token
  4. Asp.NET Identity 2 giving "Invalid Token" error

No matter what I do, it is always an Invalid Token where I can clearly see the token is 100% correct.

Any idea what am I doing wrong?

Edit - If this helps, my Startup.cs has the following Identity configurations

// For Identity
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
   {
     // configure identity options
     o.Password.RequireDigit = false;
     o.Password.RequireLowercase = false;
     o.Password.RequireUppercase = false;
     o.Password.RequireNonAlphanumeric = false;
       o.Password.RequiredLength = 6;
    })
      .AddEntityFrameworkStores<ApplicationDbContext>()
      .AddDefaultTokenProviders();
Ali
  • 2,702
  • 3
  • 32
  • 54
  • Are you running in a multi-server environment? Have you implemented a data protection repository? – ProgrammingLlama Jun 15 '21 at 02:12
  • Wait. I just noticed that 2/3 of your tags are ASP.NET and all of the questions you linked are ASP.NET. Is the ASP.NET Core tag erroneous? – ProgrammingLlama Jun 15 '21 at 02:43
  • @Llama apologies, Tags corrected, it is `asp.net core` - No I am running a single server environment in fact just an app on azure. But no matter where I run it, Localhost or Azure, results are the same. `data protection repository` is completely new to me, do I need to implement that? I tried in Postman and it always fails to validate the token. Also if this helps, the Mobile number verification (with PIN code) works just fine, its just the email code which does not work. – Ali Jun 15 '21 at 04:55
  • Basically tokens are encrypted. If the app gets restarted in the meantime, and it's not set up right, then it might be generating new encryption keys without you having the ability to decrypt them again. It looks like you can use [KeyVault](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-5.0#protectkeyswithazurekeyvault). – ProgrammingLlama Jun 15 '21 at 05:17
  • Hi @Llama, I had a look and that is something we will be implementing in future, for now we want to keep it to default. The app hasn't been restarted or re-deployed (or going to sleep etc) while I am trying to verify the email address. What else could cause the encryption keys to reset? – Ali Jun 15 '21 at 06:03
  • If it hasn't restarted, then I'm not entirely sure where it would be failing. Can you generate it and immediately use it in the same method? – ProgrammingLlama Jun 15 '21 at 06:12
  • I am using postman to verify it straight away, within 30 seconds. No luck. The phone verification works and I am using the default Identity token provider for that also. – Ali Jun 15 '21 at 06:13
  • If you hit the API with the same code 10 times in a row does it ever succeed? Maybe you could an endpoint to echo back the code to see if it's being received correctly (and obviously remove it after testing). – ProgrammingLlama Jun 15 '21 at 06:15
  • @Llama yes, I try to send many requests, no luck and yes I echo back the code, it is the correct code, also storing both codes (generated and code sent for verification) to SQL database for comparing, exactly the same correct code :( – Ali Jun 16 '21 at 01:58
  • Can you try [this](https://pastebin.com/TvbWYx5s). I've created a dummy dataprotection provider (i.e. it doesn't protect the data, it just returns what is passed to it verbatim). Warning: ONLY use this for testing this problem, since a malicious user could change the data to reset other users' passwords, etc. Using this we can confirm or rule out the data protection as the source of your issues. – ProgrammingLlama Jun 16 '21 at 02:34
  • Did that test shed any light on your issue? – ProgrammingLlama Jun 17 '21 at 03:47
  • @Llama will test it in few hours :) will let you know for sure – Ali Jun 17 '21 at 05:43

2 Answers2

0

There are a few questions about this implementation before I answer.

  1. Do you implement a custom DataProtectionProvider?
  2. Do you override the default token life time is x number of hours?

What you can do over come this issue, create a customer DataProtectionProvider like below,

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

Then inject in configure services with lifespan is 2 hours.

 services.AddTokenProvider<EmailConfirmationTokenProvider<User>>("emailconfirmation");

services.Configure(opt => opt.TokenLifespan = TimeSpan.FromHours(3));

Thank you

RRaveen
  • 60
  • 2
  • 7
0

In my case the problem was that i got token from query ([FromQuery] attribute) and that controller already runs UrlDecode() automatically, so i ran it twice. check tokens on createing and on recieving if they are the same.

vilem cech
  • 123
  • 7