0
votes

I am recently getting an error "Invalid token" when verifying email using asp.net identity.

I have elaborate flow as below:

Whenever user signup then the system will send an email for verify account.

string emailConfirmationToken = await _userManager.GenerateEmailConfirmationTokenAsync(userId);

After the signup, the user can add the phone number without verifying the email account and then the system sends the verification code to the phone number.

string phoneNumberVerifyCode = await _userManager.GenerateChangePhoneNumberTokenAsync(loginUserId, model.phone);

The issue is that if the user first verifies the phone number then confirm email showing "token is invalid" error and if user verifies email first then "phone number code is invalid".

Can you please help me out of this?

Thanks in advance!

1

1 Answers

0
votes

I had similar problems when creating both the Email and SMS messages right after user account creation. Email verification would fail if Phone was verified first. However if E-mail was verified first, the SMS verification still worked in my case.

What I ended up doing (which makes more sense from an account security standpoint) was to not send the e-mail verification until the phone code was verified. This way anytime someone changes their phone number, I force re-verification of e-mail as this will both notify user of changes (in case of a sim attack, they get an e-mail) and keeps a second communication method validated.

I put the E-mail creation into the SMS verification method:

protected void Code_Click(object sender, EventArgs e)
{
    if (!ModelState.IsValid)
    {
        ModelState.AddModelError("", "Invalid code");
        return;
    }

    var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
    var signInManager = Context.GetOwinContext().Get<ApplicationSignInManager>();

    var result = manager.ChangePhoneNumber(User.Identity.GetUserId(), PhoneNumber.Value, Code.Text);

    if (result.Succeeded)
    {
        var userInfo = manager.FindById(User.Identity.GetUserId());

        if (userInfo != null)
        {
            signInManager.SignIn(userInfo, isPersistent: false, rememberBrowser: false);
            // force 2FA login with successful phone number
            manager.SetTwoFactorEnabled(User.Identity.GetUserId(), true);

            // force new cell phone changes to un-confirm & re-verify email for security reasons
            userInfo.EmailConfirmed = false;
            var EmCode = manager.GenerateEmailConfirmationToken(User.Identity.GetUserId());
            var callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(EmCode, User.Identity.GetUserId(), Request);
            manager.SendEmail(User.Identity.GetUserId(), "Confirm your account", "A phone number has been added to your account. For security reasons, please confirm your email by clicking <a href=\"" + callbackUrl + "\">here</a>.");

            Response.Redirect("/Account/Manage?m=AddPhoneNumberSuccess");
        }
    }

    ModelState.AddModelError("", "Failed to verify phone");
}

You could also do the opposite and place SMS code sending in the e-mail verification method if email verification was more important than SMS, but wanted both. In my case phone number was more important than e-mail, so it got priority in the registration process.

Now that I look back on it, I'm glad it didn't work the way I originally planned, as this method is more straightforward and the user is less likely to forget to do one or the other. This solution walks them through it instead of bombarding a user with tasks.