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!!