December 2017 : Online courses in ASP.NET MVC and Angular 4. Conducted by Bipin Joshi. Read more...
Registration for December 2017 batches of ASP.NET MVC / Core and Angular 4 online courses have already started. Conducted by Bipin Joshi. Book your seat today ! Click here for more details.

ASP.NET Core Identity - Add Email Confirmation

In the previous article you learnt to implement the ASP.NET Core identity in your web applications. In most of the real world cases you would like to confirm that the email address entered by the user at the time of creating the account genuinely belongs to him. So, verifying the user's email address becomes important. Luckily, ASP.NET Core provides an easy way to do just that. In this article you will modify the application developed earlier to add email verification step.

The email verification step in ASP.NET Core Identity works as follows:

  • You generate an email verification token - an encrypted value - using ASP.NET Core Identity.
  • You then send an email to the user's email address with a link that contains the user's ID and token generated above
  • The user clicks on the email verification link and if all goes well his email is marked as verified.
  • When the user attempts to log-in to the system you check whether his email is verified or not and accordingly allow or deny the access.

Although ASP.NET Core Identity provides most of the functionality needed to implement the above steps, you need an external help. You need a mechanism to send emails through your code. In .NET Framework we used System.Net.Mail classes to do that. There is no direct equivalent in .NET Core. You will need to use some third-party NuGet packages. For the sake of our example I am going to stick with SmtpClient class from .NET Framework's System.Net.Mail namespace. Remember, however, that we do this purely to remain focus on the main topic of this article. You can substitute a third-party email sending component at a later time.

Ok. let's start.

First of all modify the Project.json to use .NET Framework rather than .NET Core. You can do so like this :

"frameworks": {
  "net452": {
    "frameworkAssemblies": {
      "System.Net": "4.0.0.0"
    }
  }
}

As you can see the frameworks section now specifies net452 as the target framework. Then add a reference to System.Net assembly using the familiar Add Reference dialog. Doing so will add the frameworkAssemblies key to the net452 as shown above.

Then open the AccountController and modify its Register() POST action as shown below:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Register(RegisterViewModel obj)
{
    if (ModelState.IsValid)
    {
        MyIdentityUser user = new MyIdentityUser();
        user.UserName = obj.UserName;
        user.Email = obj.Email;
        user.FullName = obj.FullName;
        user.BirthDate = obj.BirthDate;

        IdentityResult result = userManager.CreateAsync(user, 
        obj.Password).Result;

        if (result.Succeeded)
        {
            if(!roleManager.RoleExistsAsync("NormalUser").Result)
            {
                MyIdentityRole role = new MyIdentityRole();
                role.Name = "NormalUser";
                role.Description = "Perform normal operations.";
                IdentityResult roleResult = 
                        roleManager.CreateAsync(role).Result;
                if(!roleResult.Succeeded)
                {
                    ModelState.AddModelError("", 
                    "Error while creating role!");
                    return View(obj);
                }
            }
            userManager.AddToRoleAsync(user, "NormalUser").Wait();

            //send confirmation email

            string confirmationToken = userManager.
                 GenerateEmailConfirmationTokenAsync(user).Result;

            string confirmationLink = Url.Action("ConfirmEmail", 
              "Account", new { userid = user.Id, 
               token = confirmationToken }, 
               protocol: HttpContext.Request.Scheme);

            SmtpClient client=new SmtpClient();
            client.DeliveryMethod = SmtpDeliveryMethod.
             SpecifiedPickupDirectory;
            client.PickupDirectoryLocation = @"C:\Test";

            client.Send("test@localhost",user.Email,
                   "Confirm your email",
               confirmationLink);

            return RedirectToAction("Login", "Account");
        }
    }
    return View(obj);
}

Notice the code marked in bold letters (the other code remains unchanged).

The code calls the GenerateEmailConfirmationTokenAsync() method of UserManager class by passing the MyIdentityUser object. This call returns an encrypted confirmation token for that specific user. For verifying an email address you need user's Id and his confirmation token. A URL is formed using Url.Action() that points to the ConfirmEmail action of the Account controller. You will write this action shortly. The URL contains the user's Id and the confirmation token in the query string.

Then the code creates an SmtpClient object and configures it in such a way that outgoing emails are stored in the Test folder. Remember, again, that we do this just for testing. In a real application you will obviously need a better way like a third-party component. Then the code sends an email using the Send() method of SmtpClient. The from address, to address, subject and the body is specified as shown.

The above code will cause an email to be sent to the user with a URL. You can go to your C:\Test and open the email stored there in any text editor such as Notepad. A sample verification URL is shown below:

http://localhost:49310/Account/
ConfirmEmail?userid=d333fcd6-ac33-4d16-b17e-ed4096a567de&token=....

For the sake of clarity the actual token is not shown above. But you can see how the query string contains "userid" and "token" values.

Clicking on this link will take the user to ConfirmEmail() action of Account controller. The ConfirmEmail() action is shown below:

public IActionResult ConfirmEmail(string userid,string token)
{
    MyIdentityUser user= userManager.FindByIdAsync(userid).Result;
    IdentityResult result= userManager.
                ConfirmEmailAsync(user,token).Result;
    if(result.Succeeded)
    {
        ViewBag.Message = "Email confirmed successfully!";
        return View("Success");
    }
    else
    {
        ViewBag.Message = "Error while confirming your email!";
        return View("Error");
    }
}

The ConfirmEmail() action receives the user's ID and confirmation token from the query string. Inside, the code finds the MyIdentityUser whose Id matches with the one sent through the query string. Then ConfirmEmail() method of UserManager is called to confirm the user's email The ConfirmEmail() method requires the MyIdentityUser object and the confirmation token.

The result of ConfirmEmail() is checked and if all goes well a success view is displayed in the browser. The following figure shows a sample run of the application.

Now the final step. You need to add some checking in the Login() action that checks whether a user's email has been verified or not. So, open the Login() POST action and modify it as shown below:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Login(LoginViewModel obj)
{
    if (ModelState.IsValid)
    {
        var user = userManager.FindByNameAsync
       (obj.UserName).Result;
        if (user != null)
        {
            if (!userManager.IsEmailConfirmedAsync
                 (user).Result)
            {
                ModelState.AddModelError("", 
                "Email not confirmed!");
                return View(obj);
            }
        }


        var result = loginManager.PasswordSignInAsync
        (obj.UserName, obj.Password, 
         obj.RememberMe,false).Result;

        if (result.Succeeded)
        {
            return RedirectToAction("Index", "Home");
        }

        ModelState.AddModelError("", "Invalid login!"); 
    }

    return View(obj);
}

Notice the new code marked in bold letters. It finds a MyIdentityUser based on the username value. It then checks whether that user's email address has been confirmed or not. This is done using IsEmailConfirmedAsync() method of the UserManager. If IsEmailConfirmedAsync() returns false (email not yet verified) an error message is displayed to the user, otherwise the login process continues as before. 

That's it for now! Keep coding!!


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Meditation and Mindfulness to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 29 August 2016


Tags : ASP.NET ASP.NET Core MVC C# Security