In this blog post, we’ll explore how to implement two-factor Authenticator authentication in a .NET Core MVC application. We’re dividing the blog into two parts. This is part one. We are also covering the second part in the next post.
Step By Step Guide to Implement Two-Factor Authenticator Authentication
Step 1: Create ASP.NET Core Web App using Visual Studio 2022
Open visual studio 2022 community and click on “create a new project” and select “ASP.NET Core Web App(Model-View-Controller) ” project and click next.

In the “configure your new project”, enter the name, location, and solution name of your project and click next.
In the “Additional information” step, choose “.NET 8.0 in “Framework” dropdown, None in “Authentication Type” and click on create.
Step 2: Open appsettings.json and add following ConnectionString with your own database at the end of file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"ApplicationDatabase": "Data Source=************;Initial Catalog=AuthDemoDB;Integrated Security=True;Encrypt=False;"
}
}
Step 3: Install the NuGet packages:
1. Microsoft.EntityFrameworkCore
2. Microsoft.EntityFrameworkCore.SqlServer
3. Microsoft.EntityFrameworkCore.Tools
4. BCrypt.Net-Next
Step 4: Adding Entities:
Create a class file with the name User in the Entities folder and then copy and paste the following code.
User.cs
public class User
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
Step 5: Adding Context:
Create the Context Class AppDbContext in Entities folder for Entity Framework Code First Approach:
AppDbContext.cs
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
namespace TwoFactorAuth.Entities
{
public class AppDbContext
{
public class AppDBContext : DbContext
{
public AppDBContext(DbContextOptions options) : base(options)
{
}
public virtual DbSet Users { get; set; }
}
}
}
In ASP.NET Core, services such as the DB context must be registered with the dependency injection container. Update Program.cs with the following code:
builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString("ApplicationDatabase")));
Step 6: Update Database using Migration:
Migrations are used in Entity Framework Core to apply changes to the database schema as the data model changes. This can be done using the following command in the Package Manager Console (Tools > NuGet Package Manager > Package Manager Console):
– Add-Migration InitialCreate
– Update-Database
After done update database command will execute successfully, just check the database’s table

Step 7: Create Viewmodels RegisterViewModel, LoginViewModel in Models folder: RegisterViewModel.cs
namespace TwoFactorAuth.Models
{
public class RegisterViewModel
{
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
}
LoginViewModel.cs
namespace TwoFactorAuth.Models
{
public class LoginViewModel
{
public string Username { get; set; }
public string Password { get; set; }
}
}
Step 8: Create UserService class in Services folder and add method RegisterUser, AuthenticateUser as follows:
UserService.cs
using Microsoft.EntityFrameworkCore;
using TwoFactorAuth.Entities;
using TwoFactorAuth.Models;
namespace TwoFactorAuth.Services
{
public interface IUserService
{
Task RegisterUser(RegisterViewModel registerRequest);
}
public class UserService : IUserService
{
private AppDbContext _dbContext;
public UserService(AppDbContext appDBContext)
{
_dbContext = appDBContext;
}
public async Task RegisterUser(RegisterViewModel registerRequest)
{
try
{
bool isExist = await _dbContext.Users.AnyAsync(u => u.Username == registerRequest.Username);
if (isExist)
{
return false;
}
User user = new User();
user.Name = registerRequest.Name;
user.Username = registerRequest.Username;
user.Password = BCrypt.Net.BCrypt.HashPassword(registerRequest.Password);
var res = await _dbContext.Users.AddAsync(user);
await _dbContext.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
return false;
}
}
public async Task AuthenticateUser(LoginViewModel loginRequest)
{
try
{
var user = await _dbContext.Users.FirstOrDefaultAsync(m => m.Username == loginRequest.Username);
if (user != null && BCrypt.Net.BCrypt.Verify(loginRequest.Password, user.Password))
{
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
return false;
}
}
}
}
To Implement password hashing BCrypt.Net.BCrypt.HashPassword & BCrypt.Net.BCrypt.Verify methods used.
Step 9: Register Userservice with the dependency injection container in program.cs file as follows:
builder.Services.AddScoped();
Step 10 : Configure Cookie authentication in Program.cs:
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = "/Account/Login/";
});
Step 11 : Add Controller named AccountController in Controllers folder and implement Regiser & Login action as follows:
AccountController.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using TwoFactorAuth.Entities;
using TwoFactorAuth.Models;
using TwoFactorAuth.Services;
namespace TwoFactorAuth.Controllers
{
public class AccountController : Controller
{
private readonly IUserService _userService;
public AccountController(IUserService userService)
{
_userService = userService;
}
[HttpGet]
public async Task Register()
{
return View();
}
[HttpPost]
public async Task Register(RegisterViewModel request)
{
if (ModelState.IsValid)
{
bool registrationStatus = await _userService.RegisterUser(request);
if (registrationStatus)
{
ModelState.Clear();
TempData["Success"] = "Registration Successful!";
return View();
}
else
{
TempData["Fail"] = "Registration Failed.";
return View(request);
}
}
return View(request);
}
[HttpGet]
public async Task Login()
{
return View();
}
[HttpPost]
public async Task Login(LoginViewModel request)
{
if (ModelState.IsValid)
{
bool isAuthenticaed = await _userService.AuthenticateUser(request);
if (isAuthenticaed)
{
var claims = new List
{
new Claim(ClaimTypes.Name, request.Username)
};
ClaimsIdentity userIdentity = new ClaimsIdentity(claims, "login");
ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
await HttpContext.SignInAsync(principal);
return RedirectToAction("Index", "Home");
}
else
{
TempData["UserLoginFailed"] = "Login Failed.Please enter correct credentials";
return View(request);
}
}
return View(request);
}
[HttpPost]
public async Task Logout()
{
await HttpContext.SignOutAsync();
return RedirectToAction("Index", "Home");
}
}
}
Step 12 : Add Register & Login View in Account folder under Views folder as follows: Register.cshtml
@model TwoFactorAuth.Models.RegisterViewModel
@{
ViewData["Title"] = "Register";
}
Register - New User
@if (TempData["Success"] != null)
{
@TempData["Success"] Click here to login
}
@if (TempData["Fail"] != null)
{
@TempData["Fail"]
}
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Login.cshtml
@model TwoFactorAuth.Models.LoginViewModel
@{
ViewData["Title"] = "Login";
}
Login
@if (TempData["UserLoginFailed"] != null)
{
@TempData["UserLoginFailed"]
}
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Step 13: Update _Layout page as follows:
_Layout.cshtml
@ViewData["Title"] - TwoFactorAuth
@RenderBody()
@await RenderSectionAsync("Scripts", required: false)
Step 14 : Add Authorize Attribute on HomeController:

Run the application, it’s redirect to login page.

By clicking SignUp button or Register menu on top navigate to register page:

Input required info and click register button. Will save the user info.

Now navigate to the login page and input username , password and click the Login button.

After successful login. Home page will be shown:

If you need assistance with implementing two-factor authentication or any other .NET development tasks, Hire Dedicated .NET Developers for Your Project.
We offer expert services on specific requirements, ensuring your project is delivered with the highest quality and efficiency.
Contact us today to discuss your needs and let us provide you with a seamless solution.