Back to: ASP.NET Core Identity Tutorials
Verify Phone Number in ASP.NET Core Identity
In this article, I will explain how to add and verify a Phone Number in ASP.NET Core Identity by sending an SMS with a verification code to the phone number. Please read our previous article discussing how to configure SMS Service in ASP.NET Core.
Phone number verification is a crucial feature in modern web applications, enhancing security and ensuring that user accounts are associated with accurate contact details. In ASP.NET Core Identity, we can implement this by generating and sending a verification code (OTP) via SMS, and then confirming the phone number only when the user enters the correct code. This helps in preventing fake registrations, enables multi-factor authentication, and allows secure communication with users through their verified phone numbers.
How to Verify Phone Number in ASP.NET Core Identity?
ASP.NET Core Identity provides a built-in mechanism for verifying phone numbers, such as:
- Generate and send an OTP (One-Time Password) to the user’s phone.
- Allowing the user to confirm the OTP via a form.
- Marking the phone number as verified if the OTP matches.
Now, let us proceed and see how we can verify the Phone Number using ASP.NET Core Identity. Let us first understand the flow. When the user is logged in, we need to display the “Verify Phone Number” link within the profile dropdown list, as shown in the image below.
Once the User clicks on the Verify Phone Number link, it will open the following page. Here, the user needs to add or update the Phone number and click on the Confirm Phone Number button, as shown in the image below.
Note: As we are using Twilio trial account to send SMS, so, the mobile number you entered here must be registered in your Twilio account, otherwise SMS will not be sent. Also, you need to include the country code.
Verify Phone Number:
Once you click the Confirm Phone Number button, a verification code (consisting of 6 digits) will be sent to the phone number above, and the following page will then be opened. Here, you need to provide the verification code and click on the Verify Phone Number button, as shown in the image below.
If you click on the Resend Code button, you will get the following message:
Once you enter the verification code and click on the Verify Phone Number button, if the code is valid, then it will confirm the phone number and display the following message.
Creating Phone Number Input View Model:
Create a class file named PhoneNumberInputViewModel.cs within the ViewModels folder and then copy and paste the following code. This model is used to retrieve a user’s phone number and has only one property, PhoneNumber.
using System.ComponentModel.DataAnnotations; namespace ASPNETCoreIdentityDemo.ViewModels { public class PhoneNumberInputViewModel { [Phone(ErrorMessage = "Please Enter a Valid Phone Number")] [Required(ErrorMessage = "Please Enter Phone Number")] [Display(Name = "Phone Number")] public string PhoneNumber { get; set; } = null!; } }
Creating Phone Number Code View Model
Next, create a class file named PhoneNumberCodeViewModel.cs within the ViewModels folder and then copy and paste the following code. This model is used to verify a user’s phone number and has only one property, called Token.
using System.ComponentModel.DataAnnotations; namespace ASPNETCoreIdentityDemo.ViewModels { public class PhoneNumberCodeViewModel { [Required(ErrorMessage = "Verification code is required.")] [StringLength(6, MinimumLength = 6, ErrorMessage = "Verification code must be 6 digits.")] [RegularExpression(@"^\d{6}$", ErrorMessage = "Verification code must be numeric.")] [Display(Name = "Verification Code")] public string Token { get; set; } = null!; } }
Injecting the SMS Sender Service into Account Controller:
Next, we need to inject the SMS Sender Service within the AccountService class. Using this SMS Sender Service, we will verify the phone number by sending a confirmation token.
public class AccountService : IAccountService { private readonly UserManager<ApplicationUser> _userManager; private readonly SignInManager<ApplicationUser> _signInManager; private readonly IEmailService _emailService; private readonly IConfiguration _configuration; private readonly ApplicationDbContext _dbContext; private readonly ISMSSender _smsSender; public AccountService(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IEmailService emailService, IConfiguration configuration, ApplicationDbContext dbContext, ISMSSender smsSender) { _userManager = userManager; _signInManager = signInManager; _emailService = emailService; _configuration = configuration; _dbContext = dbContext; _smsSender = smsSender; } //Existing Code }
Adding Phone Number Verification Services:
Please modify the IAccountService interface as follows. Here, we are adding new service methods for phone number verification.
using ASPNETCoreIdentityDemo.ViewModels; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using System.Security.Claims; namespace ASPNETCoreIdentityDemo.Services { public interface IAccountService { Task<IdentityResult> RegisterUserAsync(RegisterViewModel model); Task<IdentityResult> ConfirmEmailAsync(Guid userId, string token); //Updating the signature Task<(SignInResult Result, bool IsPasswordExpired, int FailedAttempts, int RemainingAttempts, DateTimeOffset? LockoutEndUtc, int MaxAttempts)> LoginUserAsync(LoginViewModel model); Task LogoutUserAsync(); Task SendEmailConfirmationAsync(string email); Task<ProfileViewModel> GetUserProfileByEmailAsync(string email); AuthenticationProperties ConfigureExternalLogin(string provider, string? redirectUrl); Task<ExternalLoginInfo?> GetExternalLoginInfoAsync(); Task<SignInResult> ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent); Task<IdentityResult> CreateExternalUserAsync(ExternalLoginInfo info); Task<bool> SendPasswordResetLinkAsync(string email); Task<IdentityResult> ResetPasswordAsync(ResetPasswordViewModel model); Task<IdentityResult> ChangePasswordAsync(ChangePasswordViewModel model, HttpContext httpContext); Task<bool> HasPasswordAsync(ClaimsPrincipal principal); Task<IdentityResult> SetPasswordAsync(SetPasswordViewModel model, ClaimsPrincipal principal); Task<IdentityResult> ForceChangeExpiredPasswordAsync(string email, ChangePasswordViewModel model, HttpContext httpContext); //New Methods for Phone Number Verification Task<(bool Sent, string Message)> SendPhoneNumberVerificationCodeAsync(PhoneNumberInputViewModel model, ClaimsPrincipal principal); Task<IdentityResult> VerifyPhoneNumberCodeAsync(PhoneNumberCodeViewModel model, ClaimsPrincipal principal); } }
Providing Implementation:
Please add the following two methods within the Account Service class.
SendPhoneNumberVerificationCodeAsync
This method initiates the phone number verification process. It first fetches the currently logged-in user, updates their phone number (unconfirmed), and ensures it is not already confirmed for another user. Then it generates a 6-digit token and uses the SMS service to send the verification code to the specified phone number.
// STEP 1: Send verification token public async Task<(bool Sent, string Message)> SendPhoneNumberVerificationCodeAsync(PhoneNumberInputViewModel model, ClaimsPrincipal principal) { try { var user = await _userManager.GetUserAsync(principal); if (user == null) return (false, "User not found."); // If the phone is already CONFIRMED by another user → do not allow var takenByAnotherConfirmed = await _userManager.Users .AnyAsync(u => u.Id != user.Id && u.PhoneNumber == model.PhoneNumber && u.PhoneNumberConfirmed); if (takenByAnotherConfirmed) return (false, "This mobile number is already verified by another account."); // Allowed even if others have it UNCONFIRMED // Save/Update phone number (unconfirmed) user.PhoneNumber = model.PhoneNumber; user.PhoneNumberConfirmed = false; var update = await _userManager.UpdateAsync(user); if (!update.Succeeded) { var errors = string.Join("; ", update.Errors.Select(e => e.Description)); return (false, $"Could not save phone number. {errors}"); } // Generate code/token and send SMS var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber); var sent = await _smsSender.SendSmsAsync(model.PhoneNumber, $"Your verification code is: {code}"); if (!sent) return (false, "We couldn’t send the SMS. Please check the phone format or try again."); return (true, "We sent a verification code to your phone."); } catch (Exception) { return (false, "Unexpected error while sending the code. Please try again."); } }
VerifyPhoneNumberCodeAsync
This method is responsible for verifying the phone number using the token entered by the user. It checks if the phone number is already confirmed for another user. If not, it confirms the phone number for the current user and clears the number from other users who had it stored but not confirmed, ensuring the uniqueness of confirmed phone numbers.
// STEP 2: Confirm phone number with token public async Task<IdentityResult> VerifyPhoneNumberCodeAsync(PhoneNumberCodeViewModel model, ClaimsPrincipal principal) { var user = await _userManager.GetUserAsync(principal); if (user == null) { return IdentityResult.Failed(new IdentityError { Code = "UserNotFound", Description = "User not found." }); } // Use the phone number already stored in DB var phoneNumber = user.PhoneNumber; if (string.IsNullOrEmpty(phoneNumber)) { return IdentityResult.Failed(new IdentityError { Code = "PhoneNotSet", Description = "No phone number found for this user." }); } // If the phone is already CONFIRMED by another user → block var confirmedElsewhere = await _userManager.Users .AnyAsync(u => u.Id != user.Id && u.PhoneNumber == phoneNumber && u.PhoneNumberConfirmed); if (confirmedElsewhere) { return IdentityResult.Failed(new IdentityError { Code = "PhoneAlreadyVerified", Description = "This mobile number is already verified by another account." }); } // Proceed: confirm this phone and null-out unconfirmed duplicates in a transaction await using var tx = await _dbContext.Database.BeginTransactionAsync(); var result = await _userManager.ChangePhoneNumberAsync(user, phoneNumber, model.Token); if (!result.Succeeded) { await tx.RollbackAsync(); return IdentityResult.Failed(new IdentityError { Code = "Invalid Code", Description = "Please enter the valid code" }); } // Find other users with same phone UNCONFIRMED and clear it var duplicates = await _userManager.Users .Where(u => u.Id != user.Id && u.PhoneNumber == phoneNumber && !u.PhoneNumberConfirmed) .ToListAsync(); foreach (var dupe in duplicates) dupe.PhoneNumber = null; // Single save for all clears await _dbContext.SaveChangesAsync(); await tx.CommitAsync(); return result; }
Adding Action Methods to Account Controller
Please add the following action methods to the Account Controller.
StartPhoneNumberVerification (GET)
This action displays a form where the user can enter or update their phone number. If the phone number is already stored in the database, it pre-fills the field. It is the first step in the verification process.
// 1) Ask the user to Enter or Update the Phone number (GET) [HttpGet] public async Task<IActionResult> StartPhoneNumberVerification() { try { var email = User.FindFirstValue(ClaimTypes.Email); var user = await _accountService.GetUserProfileByEmailAsync(email!); var vm = new PhoneNumberInputViewModel { // Prefill PhoneNumber if available PhoneNumber = user?.PhoneNumber ?? string.Empty }; return View(vm); } catch (Exception ex) { _logger.LogError(ex, "Failed to load phone number input page."); TempData["StatusMessage"] = "Sorry, something went wrong loading the page."; return RedirectToAction("Index", "Home"); } }
StartPhoneNumberVerification (POST)
This action takes the submitted phone number, validates it, and calls the service method to send an OTP to the number. If successful, it redirects the user to the page where they can enter the code. If it fails, an error message is displayed.
// 2) Send the Code/Token to the provided phone number (POST) and redirect to enter-code page [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> StartPhoneNumberVerification(PhoneNumberInputViewModel model) { if (!ModelState.IsValid) return View(model); var (sent, message) = await _accountService.SendPhoneNumberVerificationCodeAsync(model, User); if (!sent) { ModelState.AddModelError(string.Empty, message); return View(model); } // We sent a verification code to your phone. TempData["StatusMessage"] = message; return RedirectToAction(nameof(VerifyPhoneNumberCode)); }
VerifyPhoneNumberCode (GET)
This action displays a form where the user can enter the OTP they received on their phone. It is the second step of the verification process and prepares the user to enter the code.
// 3) Ask the User to enter the token (GET). Page also shows a 'Resend Code' button. [HttpGet] public IActionResult VerifyPhoneNumberCode() { try { var vm = new PhoneNumberCodeViewModel(); // entering only the 6-digit code return View(vm); } catch (Exception ex) { _logger.LogError(ex, "Failed to load code verification page."); TempData["StatusMessage"] = "Sorry, something went wrong loading the page."; return RedirectToAction(nameof(StartPhoneNumberVerification)); } }
ResendPhoneNumberCode (POST)
This action allows the user to request a new OTP in case they didn’t receive or lost the first one. It retrieves the stored phone number, sends a new OTP, and redirects back to the code entry page.
// 4) Resend Token (POST) [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> ResendPhoneNumberCode() { try { // We need the current phone number (already stored during step 1). var email = User.FindFirstValue(ClaimTypes.Email); var user = await _accountService.GetUserProfileByEmailAsync(email!); if (user == null || string.IsNullOrWhiteSpace(user.PhoneNumber)) { TempData["StatusMessage"] = "No phone number found. Please enter your phone number first."; return RedirectToAction(nameof(StartPhoneNumberVerification)); } var (sent, message) = await _accountService.SendPhoneNumberVerificationCodeAsync(new PhoneNumberInputViewModel { PhoneNumber = user.PhoneNumber }, User); TempData["StatusMessage"] = message; return RedirectToAction(nameof(VerifyPhoneNumberCode)); } catch (Exception ex) { _logger.LogError(ex, "Error resending phone verification code."); TempData["StatusMessage"] = "Unexpected error while resending the code."; return RedirectToAction(nameof(VerifyPhoneNumberCode)); } }
VerifyPhoneNumberCode (POST)
This action verifies the entered OTP against the generated token. If it matches, the phone number is confirmed, and the user is redirected to a success page. If not, it displays validation errors.
// 5) Verify the Phone Number with the token (POST) [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> VerifyPhoneNumberCode(PhoneNumberCodeViewModel model) { if (!ModelState.IsValid) return View(model); try { var result = await _accountService.VerifyPhoneNumberCodeAsync(model, User); if (result.Succeeded) { // 5) After confirmation, display success message return RedirectToAction(nameof(PhoneNumberVerificationSuccess)); } foreach (var error in result.Errors) ModelState.AddModelError(string.Empty, error.Description); return View(model); } catch (Exception ex) { _logger.LogError(ex, "Error verifying phone number."); ModelState.AddModelError(string.Empty, "Unexpected error while verifying the code. Please try again."); return View(model); } }
PhoneNumberVerificationSuccess (GET)
This action displays a success page once the phone number has been verified. It informs the user that their phone number is now confirmed and linked to their account.
// 6) Success page after the phone is confirmed [HttpGet] public IActionResult PhoneNumberVerificationSuccess() { // You can also use TempData["StatusMessage"] if you prefer a banner return View(); // a simple "Phone number verified successfully!" page }
Creating Views:
Now, we will create the Required Views to implement Phone Number verifications.
StartPhoneNumberVerification.cshtml
Create a view file named StartPhoneNumberVerification.cshtml within the Views/Account folder, then copy and paste the following code. This view renders the form where users can enter or update their phone number. It uses Bootstrap styling to create a professional input form with a submit button.
@model ASPNETCoreIdentityDemo.ViewModels.PhoneNumberInputViewModel @{ ViewData["Title"] = "Verify Phone Number"; } <div class="container my-4"> <div class="row justify-content-center"> <div class="col-lg-6 col-md-8"> @if (TempData["StatusMessage"] is string status && !string.IsNullOrWhiteSpace(status)) { <div class="alert alert-info alert-dismissible fade show" role="alert"> @status <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> } <div class="card shadow-sm"> <div class="card-header bg-dark text-white"> <h5 class="mb-0"> <i class="bi bi-phone-vibrate me-2"></i> Enter or Update Your Phone Number </h5> </div> <div class="card-body"> <form asp-action="StartPhoneNumberVerification" method="post" novalidate> <div asp-validation-summary="ModelOnly" class="text-danger mb-3"></div> <div class="mb-3"> <label asp-for="PhoneNumber" class="form-label"></label> <div class="input-group"> <span class="input-group-text"><i class="bi bi-telephone"></i></span> <input asp-for="PhoneNumber" class="form-control" type="tel" placeholder="+91XXXXXXXXXX" /> </div> <span asp-validation-for="PhoneNumber" class="text-danger"></span> <div class="form-text"> Include the country code (e.g., <strong>+91</strong>). Trial SMS accounts can only text verified numbers. </div> </div> <button type="submit" class="btn btn-primary w-100"> Send Verification Code </button> </form> </div> <div class="card-footer text-muted"> We’ll send a 6-digit code to this number. </div> </div> </div> </div> </div> @section Scripts { <partial name="_ValidationScriptsPartial" /> }
VerifyPhoneNumberCode.cshtml
Create a view file named VerifyPhoneNumberCode.cshtml within the Views/Account folder, then copy and paste the following code. This view renders the form where users can input the OTP sent to their phone. It also includes a button to resend the code if needed, ensuring a smooth user experience.
@model ASPNETCoreIdentityDemo.ViewModels.PhoneNumberCodeViewModel @{ ViewData["Title"] = "Enter Verification Code"; } <div class="container my-4"> <div class="row justify-content-center"> <div class="col-lg-6 col-md-8"> @if (TempData["StatusMessage"] is string status && !string.IsNullOrWhiteSpace(status)) { <div class="alert alert-info alert-dismissible fade show" role="alert"> @status <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> } <div class="card shadow-sm"> <div class="card-header bg-dark text-white"> <h5 class="mb-0"> <i class="bi bi-shield-check me-2"></i> Verify Your Phone Number </h5> </div> <div class="card-body"> <form asp-action="VerifyPhoneNumberCode" method="post" novalidate> <div asp-validation-summary="All" class="text-danger mb-3"></div> <div class="mb-3"> <label asp-for="Token" class="form-label"></label> <div class="input-group"> <span class="input-group-text"><i class="bi bi-keypad"></i></span> <input asp-for="Token" class="form-control" inputmode="numeric" pattern="[0-9]*" maxlength="6" placeholder="Enter 6-digit code" /> </div> <span asp-validation-for="Token" class="text-danger"></span> <div class="form-text"> Check your SMS messages for the verification code. </div> </div> <button type="submit" class="btn btn-primary w-100 mb-3"> Verify Phone Number </button> </form> <form asp-action="ResendPhoneNumberCode" method="post" class="text-center"> <button type="submit" class="btn btn-link"> Didn’t receive it? Resend Code </button> </form> </div> <div class="card-footer d-flex justify-content-between"> <a asp-action="StartPhoneNumberVerification" class="btn btn-success"> <i class="bi bi-pencil-square me-1"></i> Change Number </a> <a asp-controller="Home" asp-action="Index" class="btn btn-info"> <i class="bi bi-house-door me-1"></i> Home </a> </div> </div> </div> </div> </div> @section Scripts { <partial name="_ValidationScriptsPartial" /> }
PhoneNumberVerificationSuccess.cshtml
Create a view file named PhoneNumberVerificationSuccess.cshtml within the Views/Account folder, then copy and paste the following code. This view displays a confirmation message indicating that the phone number has been successfully verified. It reassures users that the process is complete.
@{ ViewData["Title"] = "Phone Verified"; } <div class="container py-4"> <div class="row justify-content-center"> <div class="col-12 col-md-8 col-lg-6"> <div class="card shadow-sm"> <div class="card-header bg-success text-white"> <h5 class="mb-0"><i class="bi bi-check-circle me-2"></i>Phone Number Verified</h5> </div> <div class="card-body"> <div class="alert alert-success d-flex align-items-center" role="alert"> <i class="bi bi-shield-lock me-2"></i> <div>Your phone number has been verified successfully.</div> </div> <div class="d-grid d-sm-flex gap-2"> <a asp-controller="Account" asp-action="Profile" class="btn btn-primary"> <i class="bi bi-person-circle me-1"></i> Go to Profile </a> <a asp-controller="Home" asp-action="Index" class="btn btn-success"> <i class="bi bi-house me-1"></i> Home </a> </div> </div> </div> </div> </div> </div>
Modifying Layout Page:
Please modify the Layout Page as follows:
@using Microsoft.AspNetCore.Identity @inject UserManager<ApplicationUser> UserManager @{ ViewData["Title"] = ViewData["Title"] ?? "Dot Net Tutorials"; bool isAuthenticated = User?.Identity?.IsAuthenticated ?? false; bool isAdmin = User?.IsInRole("Admin") ?? false; bool isManager = User?.IsInRole("Manager") ?? false; bool isStaff = isAdmin || isManager; // Admin OR Manager // Default bool hasPassword = false; if (isAuthenticated) { var currentUser = await UserManager.GetUserAsync(User!); hasPassword = currentUser != null && await UserManager.HasPasswordAsync(currentUser); } } <!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"> <!-- Left: Primary navigation --> <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> <!-- Any authenticated user --> @if (isAuthenticated) { <li class="nav-item"> <a class="nav-link" asp-controller="Home" asp-action="SecureMethod"> Secure </a> </li> } <!-- Public --> <li class="nav-item"> <a class="nav-link" asp-controller="Home" asp-action="NonSecureMethod"> Non Secure </a> </li> <!-- Admin-only --> @if (isAdmin) { <li class="nav-item"> <a class="nav-link" asp-controller="Roles" asp-action="Index"> Roles </a> </li> <li class="nav-item"> <a class="nav-link" asp-controller="Claims" asp-action="Index"> Claims </a> </li> } <!-- Admin OR Manager --> @if (isStaff) { <li class="nav-item"> <a class="nav-link" asp-controller="Users" asp-action="Index"> Users </a> </li> } </ul> <!-- Right: Auth links / user menu --> <ul class="navbar-nav mb-2 mb-lg-0"> @if (isAuthenticated) { <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" style="min-width: 220px;"> <li> <a class="dropdown-item" href="@Url.Action("Profile", "Account")"> <i class="bi bi-person-lines-fill me-2 text-primary"></i> View Profile </a> </li> @if (hasPassword) { <li> <a class="dropdown-item" asp-controller="Account" asp-action="ChangePassword"> <i class="bi bi-key me-2 text-warning"></i> Change Password </a> </li> } else { <li> <a class="dropdown-item" asp-controller="Account" asp-action="SetPassword"> <i class="bi bi-key me-2 text-warning"></i> Set Password </a> </li> } <li> <a class="dropdown-item" asp-controller="Account" asp-action="StartPhoneNumberVerification"> <i class="bi bi-telephone me-2 text-success"></i> Verify Phone Number </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 text-danger"></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> © @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>
Now, test the application and it should work as expected.
Why Do We Need to Verify Phone Numbers in ASP.NET Core Identity?
Verifying phone numbers in ASP.NET Core Identity is a crucial step for enhancing the security, reliability, and user experience of web applications. Phone number verification serves multiple purposes that collectively contribute to safeguarding user accounts and ensuring effective communication between the application and its users. The following are the key reasons:
- Two-Factor Authentication (2FA): One of the primary reasons for phone number verification is to enable two-factor authentication. By requiring users to provide a second form of verification, such as a code sent via SMS or a call, to complement their password, you significantly reduce the risk of unauthorized access.
- Protection Against Account Takeover: Phone number verification helps in preventing account takeovers. If a user’s account is compromised, the attacker would need access to the verified phone number to perform sensitive actions like login, password resets or changes to account settings, adding an extra layer of defense.
- Account Recovery: In scenarios where users forget their passwords or lose access to their accounts, a verified phone number provides a reliable method for account recovery. By sending a recovery code via SMS or voice call, users can securely reset their passwords and regain access without relying solely on email, which might be less secure or accessible.
- User Identity Verification: Verifying phone numbers helps ensure that each user account is associated with a unique and genuine individual. This reduces the likelihood of fraudulent accounts, spam registrations, and abusive behaviour within the application. It also builds trust among users, as they know that the platform takes steps to verify identities.
- Improved Communication: A verified phone number serves as a direct and immediate communication channel between the application and the user. This can be used for sending essential alerts, updates, or reminders directly to the user’s phone. It can also be used for communicating promotions, offers, or personalized content.
By implementing phone number verification in ASP.NET Core Identity, we add an extra layer of trust and security to the application. Users can be assured that their accounts are safer, while administrators can rely on verified contact details for effective communication. The step-by-step process of sending an OTP, verifying it, and confirming the number makes the system user-friendly yet secure, striking a balance between usability and protection.
In the next article, I will discuss how to implement 2 Factor Authentication in ASP.NET Core Identity. In this article, I explain how to add and verify a Phone Number in ASP.NET Core Identity. I hope you enjoy this article on ‘How to Add and Verify a Phone Number in ASP.NET Core Identity’.