Introduction
ASP.NET Core Identity is essential for any web application requiring user authentication and authorization. It offers a seamless way to manage users, passwords, roles, claims, and tokens. This guide will help you set up and use ASP.NET Core Identity effectively in your application.
Installing ASP.NET Core Identity
Install the necessary NuGet package for ASP.NET Core Identity.
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
Configuring Identity Services
builder.Services.AddIdentity<AppUser, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 7;
options.Password.RequireUppercase = true;
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddTokenProvider<DataProtectorTokenProvider<AppUser>>(TokenOptions.DefaultProvider);
Configure Middleware
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Models for the Application
AppUser
using Microsoft.AspNetCore.Identity;
namespace A_MicrosoftAspNetCoreIdentityManagement.Models
{
public class AppUser : IdentityUser
{
[PersonalData]
public string FirstName { get; set; }
[PersonalData]
public string LastName { get; set; }
}
}
Profile
namespace A_MicrosoftAspNetCoreIdentityManagement.Models
{
public class Profile
{
public int ProfileId { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? FullName { get; set; }
public string? TagLine { get; set; }
public string? About { get; set; }
public string? Country { get; set; }
public string? City { get; set; }
public string? UserName { get; set; }
public string? PhoneNumber { get; set; }
public string? FacebookLink { get; set; }
public string? TwitterLink { get; set; }
public DateTime MemberSince { get; set; }
public string? Website { get; set; }
public string? ContactVerified { get; set; }
public string? HeaderImageUrl { get; set; }
public string? DisplayImageUrl { get; set; }
public string? ProfileUrl { get; set; }
public int ProfileBadgeId { get; set; }
//Navigational Properties
public virtual ICollection<ProfileBadge>? ProfileBadges { get; set; }
}
}
Creating the Application Database Context
using A_MicrosoftAspNetCoreIdentityManagement.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace A_MicrosoftAspNetCoreIdentityManagement.Data
{
public class AppDbContext : IdentityDbContext<AppUser>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Profile> Profiles { get; set; }
public DbSet<ProfileBadge> ProfileBadges { get; set; }
public DbSet<Badge> badges { get; set; }
public DbSet<DeactivatedProfile> DeactivatedProfiles { get; set; }
}
}
Setting Up Middleware
app.UseAuthentication();
app.UseAuthorization();
Configuring Identity Options
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.User.RequireUniqueEmail = true;
});
Creating Controllers and Views for Authentication
Account Controller
Create an AccountController to handle registration, login, and logout.
using A_MicrosoftAspNetCoreIdentityManagement.Data;
using A_MicrosoftAspNetCoreIdentityManagement.Models;
using A_MicrosoftAspNetCoreIdentityManagement.Services;
using A_MicrosoftAspNetCoreIdentityManagement.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace A_MicrosoftAspNetCoreIdentityManagement.Controllers
{
public class AccountController : Controller
{
private SignInManager<AppUser> _signInManager;
private UserManager<AppUser> _userManager;
private RoleManager<IdentityRole> _roleManager;
private readonly AppDbContext _context;
public AccountController(AppDbContext context, SignInManager<AppUser> signInManager, UserManager<AppUser> userManager, RoleManager<IdentityRole> roleManager)
{
_context = context;
_signInManager = signInManager;
_userManager = userManager;
_roleManager = roleManager;
}
public IActionResult Login()
{
if (this.User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Roles");
}
return View();
}
[HttpPost, ActionName("Login")]
public async Task<IActionResult> LoginPost(LoginViewModel loginModel)
{
if (ModelState.IsValid)
{
var profile = _context.Profiles.Where(p => p.UserName == loginModel.Email).SingleOrDefault();
var deactivated = _context.DeactivatedProfiles.Where(e => e.ProfileId == profile.ProfileId).FirstOrDefault();
if (deactivated != null)
{
ModelState.AddModelError("", $"Your profile has been blocked:{deactivated.Reason}. Please contact administrator [email protected]");
return View();
}
var result = await _signInManager.PasswordSignInAsync(loginModel.Email, loginModel.Password, loginModel.RememberMe, false);
if (result.Succeeded)
{
var user = await _userManager.FindByEmailAsync(loginModel.Email);
var roles = await _userManager.GetRolesAsync(user);
var primaryRole = roles.FirstOrDefault();
HttpContext.Session.SetString("ProfileId", profile.ProfileId.ToString());
HttpContext.Session.SetString("ProfileRole", primaryRole ?? "Member");
if (!string.IsNullOrWhiteSpace(profile.DisplayImageUrl))
{
HttpContext.Session.SetString("ProfileImage", profile.DisplayImageUrl);
}
else
{
HttpContext.Session.SetString("ProfileImage", "favicon.ico");
}
if(primaryRole == "Vendor")
{
return RedirectToAction("Index", "Roles");
}
else if (primaryRole == "Artist")
{
return RedirectToAction("BuyerDashBoard", "Roles");
}
else if (primaryRole == "Buyer")
{
return RedirectToAction("BuyerDashBoard", "Roles");
}
else if (primaryRole == "Admin")
{
return RedirectToAction("Index", "Roles");
}
else if (primaryRole == "Member")
{
return RedirectToAction("Index", "Home");
}
}
}
ModelState.AddModelError("", "Faild to Login");
return View();
}
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}
public IActionResult Register()
{
return View();
}
[HttpPost, ActionName("Register")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RegisterPost(RegisterViewModel registerModel)
{
if (ModelState.IsValid)
{
AppUser user = new AppUser
{
FirstName = registerModel.FirstName,
LastName = registerModel.LastName,
UserName = registerModel.Email,
PhoneNumber = registerModel.PhoneNumber,
Email = registerModel.Email
};
Profile profile = new Profile
{
UserName = registerModel.Email,
FirstName = registerModel.FirstName,
LastName = registerModel.LastName,
FullName = registerModel.FirstName + " " + registerModel.LastName,
PhoneNumber = registerModel.PhoneNumber
};
var result = await _userManager.CreateAsync(user, registerModel.Password);
if (result.Succeeded)
{
bool roleExists = await _roleManager.RoleExistsAsync(registerModel.RoleName);
if (!roleExists)
{
await _roleManager.CreateAsync(new IdentityRole(registerModel.RoleName));
}
if (!await _userManager.IsInRoleAsync(user, registerModel.RoleName))
{
await _userManager.AddToRoleAsync(user, registerModel.RoleName);
}
if (!string.IsNullOrWhiteSpace(user.Email))
{
// Claim[] claim = new Claim(ClaimTypes.GivenName, user.FirstName);
Claim[] claims = new Claim[]
{
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName)
};
await _userManager.AddClaimsAsync(user, claims);
}
//Add profile data
_context.Profiles.Add(profile);
await _context.SaveChangesAsync();
var resultSignIn = await _signInManager.PasswordSignInAsync(registerModel.Email, registerModel.Password, registerModel.RememberMe, false);
if (resultSignIn.Succeeded)
{
HttpContext.Session.SetString("ProfileId", profile.ProfileId.ToString());
HttpContext.Session.SetString("ProfileImage", "favicon.ico");
return RedirectToAction("Index", "Roles");
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
return View();
}
public IActionResult ChangePassword()
{
return View();
}
[HttpPost]
public async Task<IActionResult> ChangePassword(ChangePassworViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
AppUser user = await _userManager.FindByNameAsync(User.Identity.Name);
var result = await _userManager.ChangePasswordAsync(user, model.CurrentPassword, model.NewPassword);
if (result.Succeeded)
{
ViewBag.Message = "Your password has been updated";
return View();
}
return View();
}
public DeactivatedProfile DeactivatedCheck(int id)
{
return _context.DeactivatedProfiles.Where(e => e.ProfileId == id).FirstOrDefault();
}
public IActionResult VerifyContact()
{
var profile = _context.Profiles.Where(u => u.UserName == User.Identity.Name).FirstOrDefault();
VerifyContactViewModel model = new VerifyContactViewModel
{
Email = profile.UserName,
PhoneNumber = profile.PhoneNumber,
Status = profile.ContactVerified
};
return View(model);
}
[HttpPost]
public IActionResult VerifyContact(VerifyContactViewModel model)
{
if (ModelState.IsValid)
{
if (model.VerificationCode == "5186")
{
var profile = _context.Profiles.Where(u => u.UserName == User.Identity.Name).FirstOrDefault();
profile.ContactVerified = "Verified";
_context.Profiles.Update(profile);
_context.SaveChanges();
ViewBag.Message = "Contact Verified";
return View(model);
}
else
{
ModelState.AddModelError("", $"You entered wrong code.Please enter code sent on your email");
return View(model);
}
}
return View(model);
}
public async Task<string> ConfirmContact()
{
var email = User.Identity.Name;
await EmailService.SendEmailAsync(new MailRequest() { ToEmail = email, Subject = "Verification Code", Body = "Your Verification Code is:5186" });
//Send verification code
return "Verification Code Sent to your email";
}
public IActionResult ForgotPassword()
{
return View();
}
[HttpPost]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
string code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", "Account", new { Email = user.Email, Code = code }, protocol: Request.Scheme);
// await _userManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
await EmailService.SendEmailAsync(new MailRequest() { ToEmail = user.Email, Subject = "Reset Password", Body = "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>" });
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
// GET: /Account/ForgotPasswordConfirmation
[AllowAnonymous]
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public IActionResult ResetPassword(string email, string code)
{
return code == null ? View("Error") : View();
}
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.ToString());
}
}
[AllowAnonymous]
public IActionResult ResetPasswordConfirmation()
{
return View();
}
}
}
ViewModel Classes
LoginViewModel
using System.ComponentModel.DataAnnotations;
namespace A_MicrosoftAspNetCoreIdentityManagement.ViewModels
{
public class LoginViewModel
{
[Required(ErrorMessage = "Please enter your email")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Display(Name = "Password")]
[Required(ErrorMessage = "Please enter your password.")]
public string Password { get; set; }
[Display(Name = "Remember Me")]
public bool RememberMe { get; set; }
}
}
RegisterViewModel
using System.ComponentModel.DataAnnotations;
namespace A_MicrosoftAspNetCoreIdentityManagement.ViewModels
{
public class RegisterViewModel: LoginViewModel
{
[Display(Name = "First Name")]
[Required(ErrorMessage = "Please enter your first name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[Required(ErrorMessage = "Please enter your last name")]
public string LastName { get; set; }
[Display(Name = "Role Name")]
[Required(ErrorMessage = "Please select a role")]
public string RoleName { get; set; }
[Display(Name = "Phone Number")]
[Required(ErrorMessage = "Please enter your phone number")]
[DataType(DataType.PhoneNumber)]
public string PhoneNumber { get; set; }
}
}
Register and Login Views
Create views for registration and login.
Register.cshtml
@model RegisterViewModel
@{
ViewData["Title"] = "Register";
Layout = "/Pages/Shared/_Layout.cshtml";
}
<!-- FORM POPUP -->
<div class="form-popup">
<!-- /CLOSE BTN -->
<!-- FORM POPUP HEADLINE -->
<div class="form-popup-headline primary">
<h2>Register Account</h2>
<p>Register now and start making money from home!</p>
</div>
<!-- /FORM POPUP HEADLINE -->
<!-- FORM POPUP CONTENT -->
<div class="form-popup-content">
<form id="register-form4" method="post" enctype="multipart/form-data" asp-action="Register">
<div asp-validation-summary="ModelOnly" class="Error-Message"></div>
<label asp-for="FirstName" class="rl-label"></label>
<input asp-for="FirstName" type="text" placeholder="First Name">
<span asp-validation-for="FirstName" class="Error-Message"></span>
<label asp-for="LastName" class="rl-label"></label>
<input asp-for="LastName" type="text" placeholder="Last Name">
<span asp-validation-for="LastName" class="Error-Message"></span>
<label asp-for="PhoneNumber" class="rl-label"></label>
<input asp-for="PhoneNumber" type="text" placeholder="Phone Number">
<span asp-validation-for="PhoneNumber" class="Error-Message"></span>
<label asp-for="Email" class="rl-label"></label>
<input asp-for="Email" type="email" placeholder="[email protected]">
<span asp-validation-for="Email" class="Error-Message"></span>
<label asp-for="Password" class="rl-label"></label>
<input asp-for="Password" type="password" placeholder="Enter your password here...">
<span asp-validation-for="Password" class="Error-Message"></span>
<label asp-for="RoleName" class="rl-label"></label>
<select asp-for="RoleName" class="form-control">
<option value="">Select Role</option>
<option value="Member">Member</option>
<option value="Vendor">Vendor</option>
<option value="Artist">Artist</option>
</select>
<span asp-validation-for="RoleName" class="Error-Message"></span>
<button class="button mid dark">Register <span class="primary">Now!</span></button>
</form>
</div>
<!-- /FORM POPUP CONTENT -->
</div>
<!-- /FORM POPUP -->
@section Scripts {
<!-- jQuery -->
<script src="~/js/vendor/jquery-3.1.0.min.js"></script>
<script src="~/js/vendor/twitter/jquery.tweet.min.js"></script>
<!-- Side Menu -->
<script src="~/js/side-menu.js"></script>
<!-- User Quickview Dropdown -->
<script src="~/js/user-board.js"></script>
<!-- Footer -->
<script src="~/js/footer.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
}
Login.cshtml
@model LoginViewModel
@{
ViewData["Title"] = "Login";
Layout = "/Pages/Shared/_Layout.cshtml";
}
<!-- FORM POPUP -->
<div class="form-popup">
<!-- FORM POPUP HEADLINE -->
<div class="form-popup-headline secondary">
<h2>Login to your Account</h2>
<p>Enter now to your account and start buying and selling!</p>
</div>
<!-- /FORM POPUP HEADLINE -->
<!-- FORM POPUP CONTENT -->
<div class="form-popup-content">
<form id="login-form2" method="post" enctype="multipart/form-data" asp-action="Login">
<div asp-validation-summary="ModelOnly" class="Error-Message"></div>
<label asp-for="Email" class="rl-label"></label>
<input asp-for="Email" type="text" placeholder="[email protected]" required>
<span asp-validation-for="Email" class="Error-Message"></span>
<label asp-for="Password" class="rl-label"></label>
<input asp-for="Password" type="password" placeholder="Enter your password here..." required>
<span asp-validation-for="Password" class="Error-Message"></span>
<!-- CHECKBOX -->
<input asp-for="RememberMe" type="checkbox" checked>
<label asp-for="RememberMe" class="label-check">
<span class="checkbox primary primary"><span></span></span>
Remember username and password
</label>
<span asp-validation-for="RememberMe" class="Error-Message"></span>
<!-- /CHECKBOX -->
<p>Forgot your password? <a asp-action="ForgotPassword" asp-controller="Account" class="primary">Click here!</a></p>
<button class="button mid dark">Login <span class="primary">Now!</span></button>
</form>
<!-- LINE SEPARATOR -->
<hr class="line-separator double">
</div>
<!-- /FORM POPUP CONTENT -->
</div>
<!-- /FORM POPUP -->
@section Scripts {
<!-- jQuery -->
<script src="~/js/vendor/jquery-3.1.0.min.js"></script>
<script src="~/js/vendor/twitter/jquery.tweet.min.js"></script>
<!-- Side Menu -->
<script src="~/js/side-menu.js"></script>
<!-- User Quickview Dropdown -->
<script src="~/js/user-board.js"></script>
<!-- Footer -->
<script src="~/js/footer.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
}
Adding Email Confirmation and Password Recovery
Enable email confirmation and password recovery to enhance security.
Email Configuration
namespace A_MicrosoftAspNetCoreIdentityManagement.Configurations
{
public class EmailConfiguration
{
public static string SenderEmail = "[email protected]";
public static string Password = "test123/";
public static string Host = "smtp.gmail.com";
public static int Port = 465;
public static bool UseSsl = true;
public static string DisplayName = "MVC Support";
}
}
Email Service
using A_MicrosoftAspNetCoreIdentityManagement.Configurations;
using MailKit.Net.Smtp;
using MimeKit;
namespace A_MicrosoftAspNetCoreIdentityManagement.Services
{
public static class EmailService
{
public async static Task SendEmailAsync(MailRequest mailRequest)
{
var email = new MimeMessage();
email.Sender = MailboxAddress.Parse(EmailConfiguration.SenderEmail);
email.To.Add(MailboxAddress.Parse(mailRequest.ToEmail));
email.Subject = mailRequest.Subject;
var builder = new BodyBuilder();
if (mailRequest.Attachments != null)
{
byte[] fileBytes;
foreach (var file in mailRequest.Attachments)
{
if (file.Length > 0)
{
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
fileBytes = ms.ToArray();
}
builder.Attachments.Add(file.FileName, fileBytes, ContentType.Parse(file.ContentType));
}
}
}
builder.HtmlBody = mailRequest.Body;
email.Body = builder.ToMessageBody();
using var smtp = new SmtpClient();
smtp.Connect(EmailConfiguration.Host, EmailConfiguration.Port, EmailConfiguration.UseSsl);
smtp.Authenticate(EmailConfiguration.SenderEmail, EmailConfiguration.Password);
await smtp.SendAsync(email);
smtp.Disconnect(true);
}
}
public class MailRequest
{
public string ToEmail { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public List<IFormFile> Attachments { get; set; }
}
}
Enable Password Recovery
Add actions for password recovery.
[HttpPost]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
string code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", "Account", new { Email = user.Email, Code = code }, protocol: Request.Scheme);
// await _userManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
await EmailService.SendEmailAsync(new MailRequest() { ToEmail = user.Email, Subject = "Reset Password", Body = "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>" });
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
Output
If our role is vendor, then the vendor dashboard will be shown on the screen.
If our role is buyer, then the buyer dashboard will be shown on the screen.
Github Project Link
https://github.com/SardarMudassarAliKhan/A-MicrosoftAspNetCoreIdentityManagement
Conclusion
ASP.NET Core Identity is a powerful and flexible framework for handling authentication and authorization in ASP.NET Core applications. By following this guide, you can set up and customize ASP.NET Core Identity to meet the needs of your application, providing a secure and user-friendly experience for your users.