Register, Login, and Logout using ASP.NET Core Identity

Register, Login, and Logout in ASP.NET Core Identity

In this article, I will discuss how to implement Register, Login, and Logout using ASP.NET Core Identity. Please read our previous article discussing Customizing ASP.NET Core Identity Database Tables.

What is Register and Why?

Register (or Registration) is the process of creating a new user account in a system. During registration, users provide necessary information such as their name, email, and password, which is then stored securely to create a unique identity for future authentication.

Why Register?
  • To establish a unique identity for the user in the system.
  • To collect essential data needed for personalized services and communication.
  • To enable users to securely access and manage their information over time.
  • Registration is the first step before a user can log in and use restricted parts of the application.
What is Login and Why?

Login is the process by which a user provides credentials (typically a username/email and password) to access a secure system or application. It verifies the identity of the user to ensure only authorized individuals can enter and use protected resources.

Why Login?
  • To authenticate the user and confirm they are who they claim to be.
  • To protect sensitive data and functionality from unauthorized access.
  • To enable user-specific actions like managing profiles, viewing private content, or performing transactions.
What is Logout and Why?

Logout is the action by which a logged-in user intentionally ends their authenticated session, effectively disconnecting from the system and requiring login again to regain access.

Why Logout?
  • To protect the user’s account from unauthorized use, especially on shared or public devices.
  • To clear session data and tokens that represent the logged-in state, to improve security.
  • To signal the end of a user’s interaction with the system, allowing clean resource management on the server side.
  • To ensure that no one else can access the account without re-authenticating.

Let us first understand the different pages that we are going to develop.

User Registration Page:

This page allows new users to create an account by providing essential details such as name, email, phone number, and password. It validates inputs to ensure data correctness and initiates the registration process by storing user data securely.

User Registration Page

Registration Success Page:

Displayed immediately after successful registration, this page thanks the user for signing up and informs them that a confirmation email has been sent to their inbox. It guides users to check their email and activate their account via the verification link.

Registration Success Page

Sending Email Verification Mail:

This process involves sending an email containing a secure confirmation link to the user’s registered email address. The email prompts users to verify their ownership of the email address and activate their account.

Sending Email Verification Mail

Email Confirmed Page:

After clicking the verification link, users are directed to this page, confirming their email has been successfully verified and their account is now active. It provides a clear message and a link to log in and start using the application.

Email Confirmed Page

Account Created Confirmation Email:

After successful email confirmation, the system sends a confirmation email to the user. This email notifies users that their account is ready and provides a warm welcome, along with links to log in or get started.

Account Created Confirmation Email

Resend Confirmation Email Page:

This page allows users who may have missed or not received the original email confirmation link to request a new one. It helps ensure all users can activate their accounts and access the platform, even if technical issues occurred previously.

Resend Confirmation Email Page

Resend Email Confirmation Success Page:

After a user requests to resend the confirmation email, this page lets them know that, if their email is registered, a new confirmation email has been sent. It encourages them to check their inbox (and spam/junk folders) for the email.

Resend Email Confirmation Success Page

Login Page:

The Login page allows registered and confirmed users to access their accounts using their email and password securely. It may also offer options for social login and provide links to register or resend confirmation emails as needed.

Login Page

Account Profile Page:

This page displays the logged-in user’s personal information, such as name, email, phone number, and login history. It provides options to edit profile details or change the password, empowering users to maintain their account security and accuracy.

Account Profile Page

Define View Models for Registration and Login

We create separate ViewModels with proper validation (DataAnnotations) to separate UI concerns from domain models.

  • ViewModels help to decouple the UI from the database entities.
  • Use DataAnnotations like [Required], [EmailAddress], [StringLength], and custom validation for passwords.

First, create a folder named ViewModels in the project root directory, where we will create all our View Models.

Creating RegisterViewModel Class

Create a class file named RegisterViewModel.cs in the ViewModels folder, and copy and paste the following code. It defines the data structure and validation rules for user registration input fields, such as First Name, Last Name, Email, Phone Number, Date of Birth, Password, and Confirm Password. Ensures proper input validation using DataAnnotations.

using System.ComponentModel.DataAnnotations;

namespace ASPNETCoreIdentityDemo.ViewModels
{
    public class RegisterViewModel
    {
        [Required]
        [Display(Name = "First Name")]
        [StringLength(50, ErrorMessage = "First Name cannot be longer than 50 characters.")]
        public string FirstName { get; set; } = null!;

        [Display(Name = "Last Name")]
        [StringLength(50, ErrorMessage = "Last Name cannot be longer than 50 characters.")]
        public string? LastName { get; set; }

        [Required(ErrorMessage ="Email Id is Required")]
        [EmailAddress(ErrorMessage = "Invalid Email Address")]
        public string Email { get; set; } = null!;

        [DataType(DataType.Date)]
        [Display(Name = "Date of Birth")]
        public DateTime? DateOfBirth { get; set; }

        [Required(ErrorMessage = "PhoneNumber is Required")]
        [Phone(ErrorMessage = "Please enter a valid Phone number")]
        [Display(Name = "Phone Number")]
        public string PhoneNumber { get; set; } = default!;

        [Required]
        [DataType(DataType.Password)]
        [StringLength(100, MinimumLength = 6, ErrorMessage = "Password must be at least 6 characters.")]
        public string Password { get; set; } = null!;

        [Required]
        [DataType(DataType.Password)]
        [Compare("Password", ErrorMessage = "Password and confirmation password do not match.")]
        [Display(Name = "Confirm Password")]
        public string ConfirmPassword { get; set; } = null!;
    }
}
Creating Login View Model

Create a class file named LoginViewModel.cs within the ViewModels folder and then copy and paste the following code. It defines the data structure and validation rules for login inputs, including Email, Password, and the Remember Me checkbox. Separates UI concerns from domain entities for the login form.

using System.ComponentModel.DataAnnotations;

namespace ASPNETCoreIdentityDemo.ViewModels
{
    public class LoginViewModel
    {
        [Required(ErrorMessage = "Email Id is Required")]
        [EmailAddress(ErrorMessage = "Invalid Email Address")]
        public string Email { get; set; } = null!;

        [Required(ErrorMessage = "Password Id is Required")]
        [DataType(DataType.Password)]
        public string Password { get; set; } = null!;

        [Display(Name = "Remember Me")]
        public bool RememberMe { get; set; }
    }
}
Creating Resend Confirmation Email View Model

Create a class file named ResendConfirmationEmailViewModel.cs within the ViewModels folder and then copy and paste the following code. It represents the data required for resending the email confirmation link, mainly just the Email field with validation.

using System.ComponentModel.DataAnnotations;

namespace ASPNETCoreIdentityDemo.ViewModels
{
    public class ResendConfirmationEmailViewModel
    {
        [Required(ErrorMessage = "Email Id is Required")]
        [EmailAddress(ErrorMessage = "Invalid Email Address")]
        public string Email { get; set; } = null!;
    }
}
Creating Profile View Model

Create a class file named ProfileViewModel.cs within the ViewModels folder and then copy and paste the following code. It holds user profile information to be displayed on the profile page, including Email, UserName, First Name, Last Name, Phone Number, Date of Birth, Last Logged In timestamp, and Account Created timestamp.

namespace ASPNETCoreIdentityDemo.ViewModels
{
    public class ProfileViewModel
    {
        public string Email { get; set; } = null!;
        public string UserName { get; set; } = null!;
        public string FirstName { get; set; } = null!;
        public string? LastName { get; set; }
        public DateTime? DateOfBirth { get; set; }
        public string? PhoneNumber { get; set; }
        public DateTime? LastLoggedIn { get; set; }
        public DateTime? CreatedOn { get; set; }
    }
}
Implement Email Service

Now, we will create a dedicated EmailService class that handles sending emails (confirmation and account creation). It will use

  • The IEmailSender interface abstraction for future testability and flexibility.
  • The System.Net.Mail for SMTP email sending.
  • The email content will be in HTML format with placeholders for dynamic data.

First, create a folder named Services at the project root directory, where we will create all our Service classes required by our application.

IEmailService Interface

Create an interface named IEmailService.cs within the Services folder and then copy and paste the following code. It defines contract methods for sending various emails, such as registration confirmation, account creation confirmation, and resend confirmation emails. Provides abstraction for email sending functionality.

namespace ASPNETCoreIdentityDemo.Services
{
    public interface IEmailService
    {
        Task SendRegistrationConfirmationEmailAsync(string toEmail, string firstName, string confirmationLink);
        Task SendAccountCreatedEmailAsync(string toEmail, string firstName, string loginLink);
        Task SendResendConfirmationEmailAsync(string toEmail, string firstName, string confirmationLink);
    }
}
EmailService Implementation

Create a class file named EmailService.cs within the Services folder and then copy and paste the following code. It is the concrete implementation of IEmailService that sends HTML-formatted emails using SMTP. Responsible for sending registration confirmation, account creation, and resending confirmation emails with professional templates.

using System.Net;
using System.Net.Mail;

namespace ASPNETCoreIdentityDemo.Services
{
    public class EmailService : IEmailService
    {
        private readonly IConfiguration _configuration;

        public EmailService(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public async Task SendRegistrationConfirmationEmailAsync(string toEmail, string firstName, string confirmationLink)
        {
            string htmlContent = $@"
                <html><body style='font-family: Arial, sans-serif; background-color: #f4f6f8; margin:0; padding:20px;'>
                  <div style='max-width:600px; margin:auto; background:#fff; padding:30px; border-radius:8px;'>
                    <h2 style='color:#333;'>Welcome, {firstName}!</h2>
                    <p style='font-size:16px; color:#555;'>Thank you for registering. Please confirm your email by clicking the button below.</p>
                    <p style='text-align:center;'>
                      <a href='{confirmationLink}' style='background:#0d6efd; color:#fff; padding:12px 24px; border-radius:6px; text-decoration:none; font-weight:bold;'>Confirm Your Email</a>
                    </p>
                    <p style='font-size:12px; color:#999; margin-top:30px;'>&copy; {DateTime.UtcNow.Year} Dot Net Tutorials. All rights reserved.</p>
                  </div>
                </body></html>";

            await SendEmailAsync(toEmail, "Email Confirmation - Dot Net Tutorials", htmlContent, true);
        }

        public async Task SendAccountCreatedEmailAsync(string toEmail, string firstName, string loginLink)
        {
            string htmlContent = $@"
                <html><body style='font-family: Arial, sans-serif; background-color: #f4f6f8; margin:0; padding:20px;'>
                  <div style='max-width:600px; margin:auto; background:#fff; padding:30px; border-radius:8px;'>
                    <h2 style='color:#333;'>Hello, {firstName}!</h2>
                    <p style='font-size:16px; color:#555;'>Your account has been successfully created and your email is confirmed.</p>
                    <p style='text-align:center;'>
                      <a href='{loginLink}' style='background:#198754; color:#fff; padding:12px 24px; border-radius:6px; text-decoration:none; font-weight:bold;'>Login to Your Account</a>
                    </p>
                    <p style='font-size:12px; color:#999; margin-top:30px;'>&copy; {DateTime.UtcNow.Year} Dot Net Tutorials. All rights reserved.</p>
                  </div>
                </body></html>";

            await SendEmailAsync(toEmail, "Account Created - Dot Net Tutorials", htmlContent, true);
        }

        public async Task SendResendConfirmationEmailAsync(string toEmail, string firstName, string confirmationLink)
        {
            string htmlContent = $@"
                <html><body style='font-family: Arial, sans-serif; background-color: #f4f6f8; margin:0; padding:20px;'>
                  <div style='max-width:600px; margin:auto; background:#fff; padding:30px; border-radius:8px;'>
                    <h2 style='color:#333;'>Hello, {firstName}!</h2>
                    <p style='font-size:16px; color:#555;'>You requested a new email confirmation link. Please confirm your email by clicking the button below.</p>
                    <p style='text-align:center;'>
                      <a href='{confirmationLink}' style='background:#0d6efd; color:#fff; padding:12px 24px; border-radius:6px; text-decoration:none; font-weight:bold;'>Confirm Your Email</a>
                    </p>
                    <p style='font-size:12px; color:#999; margin-top:30px;'>&copy; {DateTime.UtcNow.Year} Dot Net Tutorials. All rights reserved.</p>
                  </div>
                </body></html>";

            await SendEmailAsync(toEmail, "Email Confirmation - Dot Net Tutorials", htmlContent, true);
        }

        private async Task SendEmailAsync(string toEmail, string subject, string body, bool isBodyHtml = false)
        {
            try
            {
                var smtpServer = _configuration["EmailSettings:SmtpServer"];
                var smtpPort = int.Parse(_configuration["EmailSettings:SmtpPort"] ?? "587");
                var senderEmail = _configuration["EmailSettings:SenderEmail"];
                var senderName = _configuration["EmailSettings:SenderName"];
                var password = _configuration["EmailSettings:Password"];

                using var message = new MailMessage
                {
                    From = new MailAddress(senderEmail!, senderName),
                    Subject = subject,
                    Body = body,
                    IsBodyHtml = isBodyHtml
                };
                message.To.Add(new MailAddress(toEmail));
               
                using var client = new SmtpClient(smtpServer, smtpPort)
                {
                    Credentials = new NetworkCredential(senderEmail, password),
                    EnableSsl = true
                };

                await client.SendMailAsync(message);
            }
            catch(Exception ex) 
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}
IAccountService Interface

Create an interface named IAccountService.cs within the Services folder and then copy and paste the following code. It defines core account-related operations such as registering users, generating email confirmation tokens, confirming emails, logging in, logging out, sending confirmation emails, and fetching user profiles.

using ASPNETCoreIdentityDemo.ViewModels;
using Microsoft.AspNetCore.Identity;
namespace ASPNETCoreIdentityDemo.Services
{
    public interface IAccountService
    {
        Task<IdentityResult> RegisterUserAsync(RegisterViewModel model);
        Task<IdentityResult> ConfirmEmailAsync(Guid userId, string token);
        Task<SignInResult> LoginUserAsync(LoginViewModel model);
        Task LogoutUserAsync();
        Task SendEmailConfirmationAsync(string email);
        Task<ProfileViewModel> GetUserProfileByEmailAsync(string email);
    }
}
AccountService Implementation

Create an interface named AccountService.cs within the Services folder and then copy and paste the following code. It implements IAccountService using ASP.NET Core Identity APIs. Handles user creation, email confirmation token generation, email confirmation, login with updating last login time, logout, sending confirmation emails, and retrieving user profile data. Integrates email sending via IEmailService.

using ASPNETCoreIdentityDemo.Models;
using ASPNETCoreIdentityDemo.ViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.WebUtilities;
using System.Text;

namespace ASPNETCoreIdentityDemo.Services
{
    public class AccountService : IAccountService
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly IEmailService _emailService;
        private readonly IConfiguration _configuration;

        public AccountService(UserManager<ApplicationUser> userManager,
                              SignInManager<ApplicationUser> signInManager,
                              IEmailService emailService,
                              IConfiguration configuration)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _emailService = emailService;
            _configuration = configuration;
        }

        public async Task<IdentityResult> RegisterUserAsync(RegisterViewModel model)
        {
            var user = new ApplicationUser
            {
                UserName = model.Email,
                Email = model.Email,
                FirstName = model.FirstName,
                LastName = model.LastName,
                DateOfBirth = model.DateOfBirth,
                IsActive = true,
                PhoneNumber = model.PhoneNumber,
                CreatedOn = DateTime.UtcNow
            };

            IdentityResult result = await _userManager.CreateAsync(user, model.Password);

            if (!result.Succeeded)
                return result;

            // Assign "User" role by default
            IdentityResult roleAssignResult = await _userManager.AddToRoleAsync(user, "User");
            if (!roleAssignResult.Succeeded)
            {
                // Handle error - optionally return this failure instead
                // or log the issue and continue
                return roleAssignResult;
            }

            var token = await GenerateEmailConfirmationTokenAsync(user);

            var baseUrl = _configuration["AppSettings:BaseUrl"] ?? throw new InvalidOperationException("BaseUrl is not configured.");
            var confirmationLink = $"{baseUrl}/Account/ConfirmEmail?userId={user.Id}&token={token}";

            await _emailService.SendRegistrationConfirmationEmailAsync(user.Email, user.FirstName, confirmationLink);

            return result;
        }

        public async Task<IdentityResult> ConfirmEmailAsync(Guid userId, string token)
        {
            if (userId == Guid.Empty || string.IsNullOrEmpty(token))
                return IdentityResult.Failed(new IdentityError { Description = "Invalid token or user ID." });

            var user = await _userManager.FindByIdAsync(userId.ToString());
            if (user == null)
                return IdentityResult.Failed(new IdentityError { Description = "User not found." });

            var decodedBytes = WebEncoders.Base64UrlDecode(token);
            var decodedToken = Encoding.UTF8.GetString(decodedBytes);

            var result = await _userManager.ConfirmEmailAsync(user, decodedToken);

            if (result.Succeeded)
            {
                var baseUrl = _configuration["AppSettings:BaseUrl"] ?? throw new InvalidOperationException("BaseUrl is not configured.");
                var loginLink = $"{baseUrl}/Account/Login";

                await _emailService.SendAccountCreatedEmailAsync(user.Email!, user.FirstName!, loginLink);
            }

            return result;
        }

        public async Task<SignInResult> LoginUserAsync(LoginViewModel model)
        {
            var user = await _userManager.FindByEmailAsync(model.Email);

            if (user == null)
                return SignInResult.Failed;

            if (!await _userManager.IsEmailConfirmedAsync(user))
                return SignInResult.NotAllowed;

            var result = await _signInManager.PasswordSignInAsync(user.UserName!, model.Password, model.RememberMe, lockoutOnFailure: false);

            if (result.Succeeded)
            {
                // Update LastLogin
                user.LastLogin = DateTime.UtcNow;
                await _userManager.UpdateAsync(user);
            }

            return result;
        }

        public async Task LogoutUserAsync()
        {
            await _signInManager.SignOutAsync();
        }

        public async Task SendEmailConfirmationAsync(string email)
        {
            if (string.IsNullOrWhiteSpace(email))
                throw new ArgumentException("Email is required.", nameof(email));

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                // Prevent user enumeration by not disclosing existence
                return;
            }

            if (await _userManager.IsEmailConfirmedAsync(user))
            {
                // Email already confirmed; no action needed
                return;
            }

            var token = await GenerateEmailConfirmationTokenAsync(user);

            var baseUrl = _configuration["AppSettings:BaseUrl"] ?? throw new InvalidOperationException("BaseUrl is not configured.");
            var confirmationLink = $"{baseUrl}/Account/ConfirmEmail?userId={user.Id}&token={token}";

            await _emailService.SendResendConfirmationEmailAsync(user.Email!, user.FirstName!, confirmationLink);
        }

        public async Task<ProfileViewModel> GetUserProfileByEmailAsync(string email)
        {
            if (string.IsNullOrEmpty(email))
                throw new ArgumentException("Email cannot be null or empty.", nameof(email));

            var user = await _userManager.FindByEmailAsync(email);

            if (user == null)
                throw new ArgumentException("User not found.", nameof(email));

            return new ProfileViewModel
            {
                UserName = user.UserName ?? string.Empty,
                Email = user.Email ?? string.Empty,
                FirstName = user.FirstName,
                LastName = user.LastName,
                PhoneNumber = user.PhoneNumber,
                LastLoggedIn = user.LastLogin,
                CreatedOn = user.CreatedOn,
                DateOfBirth = user.DateOfBirth
            };
        }

        //Helper Method
        private async Task<string> GenerateEmailConfirmationTokenAsync(ApplicationUser user)
        {
            if (user == null)
                throw new ArgumentNullException(nameof(user));

            var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            var encodedToken = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token));
            return encodedToken;
        }
    }
}
AppSettings.jso file:

Please modify the AppSettings.json file as follows. The BaseUrl is used to construct URLs in emails (e.g., confirmation links). EmailSettings contains the SMTP configuration needed by your EmailService to send emails via Gmail SMTP.

{
  "ConnectionStrings": {
    "SQLServerIdentityConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=IdentityCoreDB;Trusted_Connection=True;TrustServerCertificate=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "EmailSettings": {
    "SmtpServer": "smtp.gmail.com",
    "SmtpPort": "587",
    "SenderName": "[Sender Name]",
    "SenderEmail": "[Your Gmail Account]",
    "Password": "[Your Gmail App Password]"
  },
  "AppSettings": {
    "BaseUrl": "https://localhost:7091"
  }
}
Create App Password:

Please visit the link below and create your app password

https://myaccount.google.com/apppasswords

Creating Account Controller:

Create an empty MVC controller named AccountController within the Controllers folder and then copy and paste the following code. It handles all HTTP requests for account-related actions: Register, Login, Logout, Confirm Email, Resend Confirmation Email, and View Profile. Validates user input, interacts with services, manages authentication flow, and passes data to views.

using ASPNETCoreIdentityDemo.Services;
using ASPNETCoreIdentityDemo.ViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace ASPNETCoreIdentityDemo.Controllers
{
    public class AccountController : Controller
    {
        private readonly IAccountService _accountService;
        private readonly ILogger<AccountController> _logger;

        public AccountController(IAccountService accountService, ILogger<AccountController> logger)
        {
            _accountService = accountService;
            _logger = logger;
        }

        // GET: /Account/Register
        [HttpGet]
        public IActionResult Register()
        {
            return View();
        }

        // POST: /Account/Register
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Register(RegisterViewModel model)
        {
            try
            {
                if (!ModelState.IsValid)
                    return View(model);

                var result = await _accountService.RegisterUserAsync(model);

                if (result.Succeeded)
                    return RedirectToAction("RegistrationConfirmation");

                foreach (var error in result.Errors)
                    ModelState.AddModelError("", error.Description);

                return View(model);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during registration for email: {Email}", model.Email);
                ModelState.AddModelError("", "An unexpected error occurred. Please try again later.");
                return View(model);
            }
        }

        // GET: /Account/RegistrationConfirmation
        [HttpGet]
        public IActionResult RegistrationConfirmation()
        {
            return View();
        }

        // GET: /Account/ConfirmEmail
        [HttpGet]
        public async Task<IActionResult> ConfirmEmail(Guid userId, string token)
        {
            try
            {
                if (userId == Guid.Empty || string.IsNullOrEmpty(token))
                    return BadRequest("Invalid email confirmation request.");

                var result = await _accountService.ConfirmEmailAsync(userId, token);

                if (result.Succeeded)
                    return View("EmailConfirmed");

                // Combine errors into one message or pass errors to the view
                foreach (var error in result.Errors)
                    ModelState.AddModelError("", error.Description);

                return View("Error");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error confirming email for UserId: {UserId}", userId);
                ModelState.AddModelError("", "An unexpected error occurred during email confirmation.");
                return View("Error");
            }
        }

        // GET: /Account/Login
        [HttpGet]
        public IActionResult Login()
        {
            return View();
        }

        // POST: /Account/Login
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Login(LoginViewModel model, string? returnUrl = null)
        {
            try
            {
                if (!ModelState.IsValid)
                    return View(model);

                var result = await _accountService.LoginUserAsync(model);

                if (result.Succeeded)
                {
                    if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
                        return Redirect(returnUrl);

                    return RedirectToAction("Profile", "Account"); ;
                }

                if (result.IsNotAllowed)
                    ModelState.AddModelError("", "Email is not confirmed yet.");
                else
                    ModelState.AddModelError("", "Invalid login attempt.");

                return View(model);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during login for email: {Email}", model.Email);
                ModelState.AddModelError("", "An unexpected error occurred. Please try again later.");
                return View(model);
            }
        }

        [HttpGet]
        public async Task<IActionResult> Profile()
        {
            var email = User.FindFirstValue(ClaimTypes.Email);

            if (string.IsNullOrEmpty(email))
                return RedirectToAction("Login", "Account");

            try
            {
                var model = await _accountService.GetUserProfileByEmailAsync(email);
                return View(model);
            }
            catch (ArgumentException)
            {
                return View("Error");
            }
        }

        // POST: /Account/Logout
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Logout()
        {
            try
            {
                await _accountService.LogoutUserAsync();
                return RedirectToAction("Index", "Home");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during logout");
                // Optionally redirect to error page or home with message
                return RedirectToAction("Index", "Home");
            }
        }

        // GET: /Account/ResendEmailConfirmation
        [HttpGet]
        public IActionResult ResendEmailConfirmation()
        {
            return View();
        }

        // POST: /Account/ResendEmailConfirmation
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> ResendEmailConfirmation(ResendConfirmationEmailViewModel model)
        {
            try
            {
                if (!ModelState.IsValid)
                    return View(model);

                await _accountService.SendEmailConfirmationAsync(model.Email);

                ViewBag.Message = "If the email is registered, a confirmation link has been sent.";
                return View("ResendEmailConfirmationSuccess");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending email confirmation to: {Email}", model.Email);
                ModelState.AddModelError("", "An unexpected error occurred. Please try again later.");
                return View(model);
            }
        }
    }
}
Creating Views:
Register View

Create a view file named Register.cshtml within the Views/Account folder, then copy and paste the following code. The user registration page collects user information, including name, email, phone number, date of birth, and password.

@model ASPNETCoreIdentityDemo.ViewModels.RegisterViewModel

@{
    ViewData["Title"] = "Register";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container my-3">
    <div class="row shadow-lg rounded-4 bg-white overflow-hidden mx-auto" style="max-width: 900px;">
        <!-- Left: Welcome/Branding Section -->
        <div class="col-md-5 d-none d-md-flex flex-column justify-content-center align-items-center bg-primary bg-gradient text-white py-4 px-2">
            <div class="text-center">
                <h4 class="fw-bold mb-2" style="font-size:1.3rem;">Welcome to Dot Net Tutorials</h4>
                <p class="mb-3 small">
                    Join a thriving community and access premium content.<br>
                    <span class="fst-italic">Your learning journey starts here.</span>
                </p>
                <img src="https://img.freepik.com/free-vector/sign-concept-illustration_114360-5425.jpg"
                     alt="Register" class="img-fluid mb-2" width="110" />
            </div>
        </div>
        <!-- Right: Registration Form Section -->
        <div class="col-md-7 bg-light py-4 px-3">
            <h5 class="text-center fw-bold mb-3">Create Your Account</h5>

            <form asp-action="Register" method="post" novalidate>
                @if (!ViewData.ModelState.IsValid)
                {
                    <div class="alert alert-danger alert-dismissible fade show small text-center" role="alert">
                        <strong>Please fix the errors below and try again:</strong>
                        <ul class="mb-0 mt-2">
                            @foreach (var error in ViewData.ModelState.Values.SelectMany(v => v.Errors))
                            {
                                <li>@error.ErrorMessage</li>
                            }
                        </ul>
                        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                    </div>
                }
                <div class="row">
                    <div class="mb-2 col-md-6">
                        <label asp-for="FirstName" class="form-label"></label>
                        <input asp-for="FirstName" class="form-control" placeholder="First Name" />
                        <span asp-validation-for="FirstName" class="text-danger small"></span>
                    </div>
                    <div class="mb-2 col-md-6">
                        <label asp-for="LastName" class="form-label"></label>
                        <input asp-for="LastName" class="form-control" placeholder="Last Name (optional)" />
                        <span asp-validation-for="LastName" class="text-danger small"></span>
                    </div>
                </div>
                <div class="mb-2">
                    <label asp-for="Email" class="form-label"></label>
                    <input asp-for="Email" class="form-control" placeholder="Email address" />
                    <span asp-validation-for="Email" class="text-danger small"></span>
                </div>
                <div class="mb-2">
                    <label asp-for="PhoneNumber" class="form-label"></label>
                    <input asp-for="PhoneNumber" class="form-control" placeholder="Phone Number" />
                    <span asp-validation-for="PhoneNumber" class="text-danger small"></span>
                </div>
                <div class="mb-2">
                    <label asp-for="DateOfBirth" class="form-label"></label>
                    <input asp-for="DateOfBirth" type="date" class="form-control" />
                    <span asp-validation-for="DateOfBirth" class="text-danger small"></span>
                </div>

                <div class="row">
                    <div class="mb-2 col-md-6">
                        <label asp-for="Password" class="form-label"></label>
                        <input asp-for="Password" class="form-control" type="password" placeholder="Password" />
                        <span asp-validation-for="Password" class="text-danger small"></span>
                    </div>
                    <div class="mb-2 col-md-6">
                        <label asp-for="ConfirmPassword" class="form-label"></label>
                        <input asp-for="ConfirmPassword" class="form-control" type="password" placeholder="Confirm Password" />
                        <span asp-validation-for="ConfirmPassword" class="text-danger small"></span>
                    </div>
                </div>
                <button type="submit" class="btn btn-primary w-100 fw-semibold py-2 mt-1 mb-2">
                    Register
                </button>
                <div class="text-center mt-1">
                    <span class="text-secondary small">Already have an account?</span>
                    <a asp-action="Login" class="ms-1 text-primary text-decoration-none fw-semibold small">Login</a>
                </div>
            </form>
        </div>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}
RegistrationConfirmation View

Create a view file named RegistrationConfirmation.cshtml within the Views/Account folder, then copy and paste the following code. It is the page shown after successful registration, instructing the user to check their email inbox for a confirmation link. Includes tips to check spam folders and a button to navigate to the login page.

@{
    ViewData["Title"] = "Registration Confirmation";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container d-flex justify-content-center align-items-center" style="min-height: 65vh;">
    <div class="card shadow-lg rounded-4 p-4 border-0" style="max-width: 550px; width:100%;">
        <div class="card-body text-center">
            <div class="mb-3">
                <i class="bi bi-check-circle-fill text-success" style="font-size: 3rem;"></i>
            </div>
            <h4 class="fw-bold mb-2 text-success">Thank You for Registering!</h4>
            <p class="mb-4" style="font-size:1.11rem;">
                <span class="fw-semibold text-success">
                    We've sent a confirmation email to your inbox.
                </span><br>
                <span class="fw-medium text-dark">
                    Please click the confirmation link in the email to activate your account.
                </span>
            </p>
            <div class="alert alert-info d-inline-block mx-auto mb-4 py-2 px-3" style="font-size:1.05rem;">
                <i class="bi bi-info-circle-fill me-1"></i>
                <span class="fw-semibold">Can’t find the email?</span>
                <span class="text-secondary">Check your Spam or Junk folder.</span>
            </div>
            <div>
                <a asp-action="Login" class="btn btn-primary px-4 fw-semibold">Go to Login</a>
            </div>
        </div>
    </div>
</div>
EmailConfirmed View

Create a view file named EmailConfirmed.cshtml within the Views/Account folder, then copy and paste the following code. It is the page confirming successful email verification and account activation. Provides a call-to-action button for users to log in and start using the application.

@{
    ViewData["Title"] = "Email Confirmed";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container d-flex justify-content-center align-items-center mt-4" style="min-height: 55vh;">
    <div class="card shadow-lg rounded-4 p-4 border-0" style="max-width: 520px; width:100%;">
        <div class="card-body text-center">
            <div class="mb-3">
                <i class="bi bi-patch-check-fill text-success" style="font-size: 3rem;"></i>
            </div>
            <h4 class="fw-bold mb-3 text-success">Email Verified Successfully</h4>

            <div class="alert alert-success fw-semibold mb-4" style="font-size:1.08rem;">
                Your email address has been verified and your account is now active.
            </div>

            <p class="mb-4" style="font-size:1.06rem;">
                You can now log in and explore all the features of <span class="fw-semibold text-primary">Dot Net Tutorials</span>.
            </p>
            <a asp-action="Login" class="btn btn-primary px-4 fw-semibold">Login to Your Account</a>
        </div>
    </div>
</div>
Login View

Create a view file named Login.cshtml within the Views/Account folder, then copy and paste the following code. It is the login page for returning users with input fields for email, password, and the remember me option. Includes social login placeholders, registration links, and resend confirmation pages.

@model ASPNETCoreIdentityDemo.ViewModels.LoginViewModel

@{
    ViewData["Title"] = "Login";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container mt-3 mb-3">
    <div class="row shadow-lg rounded-4 bg-white overflow-hidden mx-auto" style="max-width: 800px;">
        <!-- Left: Login Section -->
        <div class="col-md-6 py-4 px-4 bg-light d-flex flex-column justify-content-center">
            <h3 class="mb-2 text-center fw-bold text-primary">Sign in to <span class="text-dark">Dot Net Tutorials</span></h3>
            <p class="text-center text-muted mb-4 small">
                <span class="fw-semibold text-secondary">Welcome back!</span>
                Please enter your credentials below to access your account.
            </p>

            <!-- Messages Section -->
            @if (ViewBag.Message != null)
            {
                <div class="alert alert-info alert-dismissible fade show mb-3 small text-center" role="alert">
                    @ViewBag.Message
                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                </div>
            }

            @if (!ViewData.ModelState.IsValid)
            {
                <div class="alert alert-danger alert-dismissible fade show small text-center" role="alert">
                    <strong>Please fix the errors below and try again:</strong>
                    <ul class="mb-0 mt-2">
                        @foreach (var error in ViewData.ModelState.Values.SelectMany(v => v.Errors))
                        {
                            <li>@error.ErrorMessage</li>
                        }
                    </ul>
                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                </div>
            }

            <form asp-action="Login" method="post" novalidate>
                <div class="mb-3">
                    <label asp-for="Email" class="form-label fw-semibold text-primary"></label>
                    <input asp-for="Email" class="form-control" placeholder="Enter your Email Address" autocomplete="username" />
                    <span asp-validation-for="Email" class="text-danger small"></span>
                </div>
                <div class="mb-3">
                    <label asp-for="Password" class="form-label fw-semibold text-primary"></label>
                    <input asp-for="Password" class="form-control" type="password" placeholder="Enter Your Password" autocomplete="current-password" />
                    <span asp-validation-for="Password" class="text-danger small"></span>
                </div>
                <div class="form-check mb-3">
                    <input asp-for="RememberMe" class="form-check-input" />
                    <label asp-for="RememberMe" class="form-check-label small"></label>
                </div>
                <button type="submit" class="btn btn-primary w-100 fw-semibold py-2 mb-2">Sign In</button>
                <p class="mt-3 text-center mb-1">
                    <span class="text-secondary small">New to <span class="fw-semibold text-dark">Dot Net Tutorials</span>?</span>
                    <a asp-action="Register" class="text-primary ms-1 text-decoration-none fw-semibold">Create an account</a>
                </p>
                <p class="text-center mb-0">
                    <a asp-action="ResendEmailConfirmation" class="text-info text-decoration-none small">Resend email confirmation</a>
                </p>
            </form>
        </div>

        <!-- Right: External Authentication -->
        <div class="col-md-6 py-4 px-4 d-flex flex-column justify-content-center align-items-center bg-white">
            <div class="w-100" style="max-width: 280px;">
                <h5 class="mb-3 fw-bold text-primary text-center">Quick sign-in</h5>
                <div class="mb-3 text-center text-muted small">Use your social account for instant access:</div>
                <div class="d-grid gap-3 mb-3">
                    <a href="#" class="btn btn-outline-primary d-flex align-items-center justify-content-center fw-semibold">
                        <img src="https://img.icons8.com/color/32/000000/google-logo.png" class="me-2" style="width:24px" alt="Google" />
                        Google
                    </a>
                    <a href="#" class="btn d-flex align-items-center justify-content-center fw-semibold" style="background-color: #3b5998; color: #fff;">
                        <img src="https://img.icons8.com/fluency/32/000000/facebook-new.png" class="me-2" style="width:24px" alt="Facebook" />
                        Facebook
                    </a>
                    <a href="#" class="btn btn-outline-dark d-flex align-items-center justify-content-center fw-semibold">
                        <img src="https://img.icons8.com/ios-filled/32/000000/github.png" class="me-2" style="width:24px" alt="GitHub" />
                        GitHub
                    </a>
                    <a href="#" class="btn d-flex align-items-center justify-content-center fw-semibold" style="background-color: #0078d4; color: #fff;">
                        <img src="https://img.icons8.com/color/32/000000/microsoft.png" class="me-2" style="width:24px" alt="Microsoft" />
                        Microsoft
                    </a>
                </div>
                <div class="text-center text-muted small mt-4 mb-2">
                    <span>We never post anything without your permission.</span>
                </div>
                <hr class="mb-2" />
                <div class="text-center text-muted small">
                    Need help? <a href="mailto:support@dotnettutorials.net" class="text-decoration-none fw-semibold">Contact Support</a>
                </div>
            </div>
        </div>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}
Profile View

Create a view file named Profile.cshtml within the Views/Account folder, then copy and paste the following code. It is the page that displays detailed account information of the logged-in user, such as name, email, phone, date of birth, account creation, and last login timestamps. Provides buttons to edit profile and change password with a clean and professional layout.

@model ASPNETCoreIdentityDemo.ViewModels.ProfileViewModel

@{
    ViewData["Title"] = "My Profile";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container py-1" style="min-height: 68vh; max-width: 900px;">
    <div class="row justify-content-center">
        <div class="col-12">

            <!-- Welcome/Info Message -->
            <div class="alert alert-info mb-4 shadow-sm text-center" style="font-size: 1.1rem;">
                <strong>Welcome to your profile, <span class="text-primary">@Model.FirstName</span>!</strong><br />
                Here you can review your account details and keep your information up-to-date.
            </div>

            <div class="card shadow-lg border-0 rounded-4" style="background: linear-gradient(110deg, #f8fafd 70%, #e8f0fe 100%);">
                <!-- Card Header -->
                <div class="card-header bg-white text-center border-0 rounded-top-4 pb-2">
                    <h3 class="fw-bold text-primary mb-1">Account Profile</h3>
                    <span class="badge bg-primary bg-gradient fs-6 px-3 py-2 rounded-pill">
                        @Model.Email
                    </span>
                </div>
                <hr class="my-0" />
                <!-- Card Body -->
                <div class="card-body px-5 pt-4 pb-3">
                    <table class="table table-borderless mb-0" style="font-size: 1rem;">
                        <tbody>
                            <tr>
                                <th class="text-secondary fw-semibold w-50">First Name:</th>
                                <td class="fw-medium">@Model.FirstName</td>
                            </tr>
                            <tr>
                                <th class="text-secondary fw-semibold">Last Name:</th>
                                <td class="fw-medium">@Model.LastName</td>
                            </tr>
                            <tr>
                                <th class="text-secondary fw-semibold">Phone:</th>
                                <td class="fw-medium">@Model.PhoneNumber</td>
                            </tr>
                            <tr>
                                <th class="text-secondary fw-semibold">Date of Birth:</th>
                                <td class="fw-medium">
                                    @(Model.DateOfBirth.HasValue? Model.DateOfBirth.Value.ToString("dd MMM yyyy") : "-")
                                </td>
                            </tr>
                            <tr>
                                <th class="text-secondary fw-semibold">Account Created:</th>
                                <td class="fw-medium">
                                    @(Model.CreatedOn.HasValue? Model.CreatedOn.Value.ToString("dd MMM yyyy, hh:mm tt") : "-")
                                </td>
                            </tr>
                            <tr>
                                <th class="text-secondary fw-semibold">Last Logged In:</th>
                                <td class="fw-medium">
                                    @(Model.LastLoggedIn.HasValue? Model.LastLoggedIn.Value.ToString("dd MMM yyyy, hh:mm tt") : "-")
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
                <!-- Card Footer -->
                <div class="card-footer bg-white border-0 rounded-bottom-4 text-center pt-3 pb-4">
                    <a asp-action="EditProfile" class="btn btn-info text-white fw-semibold me-3 px-5 py-2 rounded-pill">
                        Edit Profile
                    </a>
                    <a asp-action="ChangePassword" class="btn btn-warning text-dark fw-semibold px-5 py-2 rounded-pill">
                        Change Password
                    </a>
                </div>
            </div>
        </div>
    </div>
</div>
ResendEmailConfirmation View

Create a view file named ResendEmailConfirmation.cshtml within the Views/Account folder, then copy and paste the following code. This page allows users to request the resending of their email confirmation link by entering their registered email address. Displays helpful messages and validation feedback.

@model ASPNETCoreIdentityDemo.ViewModels.ResendConfirmationEmailViewModel

@{
    ViewData["Title"] = "Resend Email Confirmation";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container mt-3 mb-2 d-flex justify-content-center align-items-center" style="min-height: 65vh;">
    <div class="card shadow-lg rounded-4 border-0 p-0" style="max-width: 540px; width: 100%; background: linear-gradient(120deg, #f3faff 70%, #e8f0fe 100%);">
        <div class="card-body pt-3 px-5 pb-5">
            <div class="text-center mb-1">
                <i class="bi bi-envelope-arrow-up-fill text-primary" style="font-size: 2.7rem;"></i>
            </div>
            <h3 class="card-title text-center mb-3 fw-bold text-primary">Resend Email Confirmation</h3>
            <div class="alert alert-info mb-4 py-2 px-3 text-center" style="font-size:1.05rem;">
                Please enter your <span class="fw-semibold">registered email address</span> below.<br>
                We’ll send you a new confirmation link right away.
            </div>
            @if (ViewBag.Message != null)
            {
                <div class="alert alert-success alert-dismissible fade show mb-3 small text-center" role="alert">
                    @ViewBag.Message
                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                </div>
            }
            @if (!ViewData.ModelState.IsValid)
            {
                <div class="alert alert-danger alert-dismissible fade show mb-3 small text-center" role="alert">
                    Please fix the errors below and try again.
                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                </div>
            }

            <form asp-action="ResendEmailConfirmation" method="post" novalidate>
                <div class="mb-3">
                    <label asp-for="Email" class="form-label fw-semibold text-primary"></label>
                    <input asp-for="Email" class="form-control shadow-sm" placeholder="Enter your registered email" />
                    <span asp-validation-for="Email" class="text-danger small"></span>
                </div>
                <button type="submit" class="btn btn-primary w-100 fw-semibold py-2">
                    Send Confirmation Email
                </button>
            </form>
            <div class="text-center mt-4">
                <span class="text-secondary small">Remembered your password?</span>
                <a asp-action="Login" class="ms-1 text-primary text-decoration-none fw-semibold small">Login here</a>
            </div>
        </div>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}
ResendEmailConfirmationSuccess View

Create a view file named ResendEmailConfirmationSuccess.cshtml within the Views/Account folder, then copy and paste the following code. This page confirms to users that a new confirmation email has been sent if their email is registered.

@{
    ViewData["Title"] = "Email Confirmation Sent";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container d-flex justify-content-center align-items-center" style="min-height: 65vh;">
    <div class="card shadow-lg rounded-4 border-0 p-4" style="max-width: 600px; width: 100%;">
        <div class="card-body text-center">
            <div class="mb-3">
                <i class="bi bi-envelope-check-fill text-primary" style="font-size: 2.5rem;"></i>
            </div>
            <h3 class="fw-bold mb-2 text-primary">Email Confirmation Sent</h3>
            <div class="alert alert-info mb-4 py-2 px-3 text-center" style="font-size:1.06rem;">
                If the email address you entered is registered, a confirmation link has been sent.<br>
                <span class="fw-semibold">
                    Please check your inbox and also your Spam or Junk  folder.
                </span>
            </div>
            <a asp-action="Login" class="btn btn-primary px-4 fw-semibold mt-2">Back to Login</a>
        </div>
    </div>
</div>
Modifying Error View:

Please modify the Error.cshtml view, which is available inside the Views/Shared folder as follows. It is a friendly error page displayed when unexpected errors occur.

@{
    ViewData["Title"] = "Error";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container d-flex justify-content-center align-items-center" style="min-height: 65vh;">
    <div class="card shadow-lg rounded-4 border-0 p-4" style="max-width: 540px; width: 100%; background: linear-gradient(120deg, #fff3f4 70%, #ffeaea 100%);">
        <div class="card-body text-center">
            <div class="mb-3">
                <i class="bi bi-x-octagon-fill text-danger" style="font-size: 2.8rem;"></i>
            </div>
            <h2 class="fw-bold mb-2 text-danger">Something Went Wrong</h2>
            <div class="alert alert-danger mb-4 py-2 px-3 text-center" style="font-size:1.08rem;">
                We’re sorry, but an unexpected error has occurred.<br>
                Please try again later. If the problem persists, contact our support team.
            </div>
            <a asp-action="Index" asp-controller="Home" class="btn btn-outline-danger px-4 fw-semibold mt-2">
                <i class="bi bi-arrow-left me-1"></i> Back to Home
            </a>
        </div>
    </div>
</div>
Modifying Layout View:

Please modify the _Layout.cshtml view, which is available inside the Views/Shared folder as follows. The shared layout file provides a consistent page structure, featuring a dark navbar, responsive navigation links, user authentication status, and a dropdown menu with profile/logout options. 

@{
    ViewData["Title"] = ViewData["Title"] ?? "Dot Net Tutorials";
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"] - Dot Net Tutorials</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
</head>
<body class="d-flex flex-column min-vh-100">

    <!-- Dark Navbar -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand fw-bold" href="@Url.Action("Index", "Home")">Dot Net Tutorials</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent"
                    aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarContent">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <a class="nav-link" href="@Url.Action("Index", "Home")">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="@Url.Action("About", "Home")">About</a>
                    </li>
                   
                </ul>

                <ul class="navbar-nav mb-2 mb-lg-0">
                    @if (User.Identity?.IsAuthenticated == true)
                    {
                        <li class="nav-item dropdown">
                            <a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
                               data-bs-toggle="dropdown" aria-expanded="false">
                                <i class="bi bi-person-circle me-1"></i>
                                Hello, @User.Identity.Name
                            </a>
                            <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
                                <li>
                                    <a class="dropdown-item" href="@Url.Action("Profile", "Account")">
                                        <i class="bi bi-person-lines-fill me-2"></i> Profile
                                    </a>
                                </li>
                                <li><hr class="dropdown-divider" /></li>
                                <li>
                                    <form method="post" asp-controller="Account" asp-action="Logout" class="px-3 py-1">
                                        <button type="submit" class="btn btn-link text-decoration-none w-100 text-start">
                                            <i class="bi bi-box-arrow-right me-2"></i> Logout
                                        </button>
                                    </form>
                                </li>
                            </ul>
                        </li>
                    }
                    else
                    {
                        <li class="nav-item"><a class="nav-link" href="@Url.Action("Login", "Account")">Login</a></li>
                        <li class="nav-item"><a class="nav-link" href="@Url.Action("Register", "Account")">Register</a></li>
                    }
                </ul>
            </div>
        </div>
    </nav>

    <!-- Main Content -->
    <main class="container flex-grow-1 py-3">
        @RenderBody()
    </main>

    <!-- Footer -->
    <footer class="bg-dark text-light py-4 mt-auto">
        <div class="container d-flex flex-column flex-md-row justify-content-between align-items-center">
            <div>
                &copy; @DateTime.Now.Year Dot Net Tutorials. All rights reserved.
            </div>
            <div>
                <a href="@Url.Action("Contact", "Home")" class="text-light me-3 text-decoration-none">Contact</a>
                <a href="@Url.Action("Privacy", "Home")" class="text-light me-3 text-decoration-none">Privacy Policy</a>
                <a href="@Url.Action("Terms", "Home")" class="text-light text-decoration-none">Terms of Service</a>
            </div>
        </div>
    </footer>

    <!-- Scripts -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Service Registration:

Please modify the Program class as follows to register the token generation provider. It configures and registers all necessary services (Identity, EF Core, custom services) and middleware for the ASP.NET Core application. Sets up token provider and token lifespan, routes, and error handling.

using ASPNETCoreIdentityDemo.Data;
using ASPNETCoreIdentityDemo.Models;
using ASPNETCoreIdentityDemo.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace ASPNETCoreIdentityDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            // Register Entity Framework Core with SQL Server
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(builder.Configuration.GetConnectionString("SQLServerIdentityConnection")));

            // Register ASP.NET Core Identity Services using AddIdentity
            builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
                    .AddEntityFrameworkStores<ApplicationDbContext>()
                    .AddDefaultTokenProviders();
            //By default, ASP.NET Core Identity’s email confirmation token (and other security tokens)
            //are valid for 1 day (24 hours).

            // Set token valid for 30 minutes
            builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
            {
                options.TokenLifespan = TimeSpan.FromMinutes(30);
            });

            // Registering Custom Services
            builder.Services.AddScoped<IAccountService, AccountService>();
            builder.Services.AddScoped<IEmailService, EmailService>();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            // Add Authentication and Authorization Middleware
            app.UseAuthentication();
            app.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");

            app.Run();
        }
    }
}

Now, run the application and test the functionality, and it should work as expected.

In the next article, I will discuss Custom Password Policy in ASP.NET Core IdentityIn this article, I try to explain how to implement Register, Login, and Logout using ASP.NET Core Identity. I hope you enjoy this Register, Login, and Logout using ASP.NET Core Identity article.

1 thought on “Register, Login, and Logout using ASP.NET Core Identity”

Leave a Reply

Your email address will not be published. Required fields are marked *