How to Implement Multi Factor Authentication Using Authenticator App in ASP.NET MVC Project?

Introduction

Enhancing security is crucial for any application, and multi-factor authentication (MFA) is a powerful method to achieve this. In this article, we will walk through the process of integrating MFA using an authenticator app in an ASP.NET MVC project. Whether you're starting from scratch or adding MFA to an existing project, this guide will provide clear, step-by-step instructions to help you secure your application against unauthorized access. From setting up the project to implementing the login methods and generating QR codes, we’ll cover everything you need to create a robust authentication system.

Steps to implement Multi-Factor authentication using authenticator app

Here, I will be starting from scratch, if you have a project you may skip the setup steps. Following are the steps to implement multi-factor authentication using the authenticator app in ASP.NET MVC project -

Step 1. Set Up the project

  1. In the Visual Studio, click 'Create a new project' and click 'Next.'
  2. Select 'ASP.NET Core Web App (Model-View-Controller)' in templates and click 'Next'.
  3. In the 'Configure your new project' window, name your project, here I have named it 'MultiFactorAuthenticationDemo' and clicked 'Next'.
  4. In the Additional Information window, in Framework, choose '.NET 8' and click 'Create'.
  5. Now, the project is created and the project structure looks like the below image.
    Project

Step 2. Install Packages

Now, let's install the required packages for our project.

  1. Click 'Project' and select 'Manage NuGet Packages'.
  2. In 'Browse', enter 'Google. Authenticator' in the search box.
  3. Select it and click 'Install'.
  4. A popup window will open, click, 'I accept'. Once installed, it will show in Packages under Dependency.

Step 3. Configure the Authentication key in Appsettings.json

Now, we will have to create an authentication key for our application. It can be any combination of characters, numbers, or special characters. I have created a demo key below.

 "AuthenticatorKey": "Demo123",

Step 4. Create Session

I will be using a session to store some data like username, etc. So let's add code for the session in the program.cs

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
});
// ...
app.UseSession();

Now, the session is added and is ready for use.

Step 5. Create Models

Now, let's add a model to our project.

public class LoginModel
{
   public string Username { get; set; }
   public string Password { get; set; }
}

We will use this model for storing login information like username and password.

Also, I will be using one more model which is the GlobalData model to store a bool and will be using it in the code later.

public static class GlobalData
{
    public static bool isLoggedIn { get; set; }
}

This model will store whether the user is logged in or not.

Step 6. Create a Controller and add a constructor

Let's create a controller for login and multi-factor authentication methods. I have named it 'LoginController'.

let's create a constructor and properties for it to use.

private readonly IConfiguration _configuration;
public LoginController(IConfiguration configuration)
{
    _configuration = configuration;
}

We will be using _configuration to access 'AuthenticatorKey' later on.

Step 7. Create login methods

Let's add a login method.

[HttpGet]
[Route("login")]
public IActionResult Login()
{
    GlobalData.isLoggedIn = false;
    var message = TempData["message"];
    ViewBag.Message = message;
    return View();
}

In the above code, I have created a HttpGet method to render the login view. Here, I have assigned false in 'GlobalData.isLoggedIn'. Used ViewBag to show messages on the login page, in case of top failure.

Now, let's add a post method of login.

[HttpPost]
[Route("login")]
public ActionResult Verify(LoginModel login)
{
    string? username = HttpContext.Session.GetString("Username");
    string isValidStr = HttpContext.Session.GetString("IsValidTwoFactorAuthentication");
    bool? isValidTwoFactorAuthentication = isValidStr != null ? bool.Parse(isValidStr) : (bool?)null;

    if (username == null || isValidTwoFactorAuthentication == false || isValidTwoFactorAuthentication == null)
    {
        if (login.Username == "Admin" && login.Password == "12345")
        {
            HttpContext.Session.SetString("Username", login.Username);
            return RedirectToAction("MultiFactorAuthentication");
        }
    }
    return RedirectToAction("Index");
}

In the above code, we are first checking whether there is data in session or not. If the session is null and the username and password match, then we will set the username in session and redirect to MultiFactorAuthentication for multi-factor authentication.

Step 7. Add a Multi-factor authentication method

[HttpGet]
[Route("multi-factor-authentication")]
public IActionResult MultiFactorAuthentication()
{
    if (HttpContext.Session.GetString("Username") == null)
    {
        return RedirectToAction("Login");
    }
    string? username = HttpContext.Session.GetString("Username");
    string authKey = _configuration.GetValue<string>("AuthenticatorKey");
    string userUniqueKey = username + authKey;
    // Two Factor Authentication Setup
    TwoFactorAuthenticator twoFacAuth = new();
    var setupInfo = twoFacAuth.GenerateSetupCode(
        "MultiFactorAuthenticationDemo",
        username,
        ConvertSecretToBytes(userUniqueKey, false),
        300
    );
    HttpContext.Session.SetString("UserUniqueKey", userUniqueKey);
    ViewBag.BarcodeImageUrl = setupInfo.QrCodeSetupImageUrl;
    ViewBag.SetupCode = setupInfo.ManualEntryKey;
    return View();
}

In the above code, I have created a method named 'MultiFactorAuthentication' that will render the QR code for multi-factor authentication. Here, if the username is null in session, then we will redirect to login, else get the authentication key from app settings, and make a unique key with the combination of username and authentication key. Then we will create an object of the TwoFactorAuthenticator class and using this object will create a QR code. Now, we will store these things in ViewBag to use them on the view page.

Now, let's create a post method that will verify the authentication code.

public ActionResult MultiFactorAuthenticate()
{
    var token = Request.Form["CodeDigit"];
    TwoFactorAuthenticator TwoFacAuth = new();
    string? UserUniqueKey = HttpContext.Session.GetString("UserUniqueKey");

    // bool isValid = TwoFacAuth.ValidateTwoFactorPIN(UserUniqueKey, token);
    bool isValid = TwoFacAuth.ValidateTwoFactorPIN(UserUniqueKey, token, TimeSpan.FromSeconds(90)); // will be valid for 30 seconds
    if (isValid)
    {
        HttpContext.Session.SetString("IsValidTwoFactorAuthentication", "true");
        GlobalData.isLoggedIn = true;
        return RedirectToAction("Dashboard");
    }
    TempData["message"] = "Google Two Factor PIN is expired or wrong";
    return RedirectToAction("Login");
}

In the above code, we store the code entered by the user in a variable named 'token'. Then we will create an object of the TwoFactorAuthenticator class and using this object we will validate the entered pin. If it is valid then, we will store 'IsValidTwoFactorAuthentication' in session with true value. And assign true in 'GlobalData.isLoggedIn' and redirect to the dashboard. If the pin is wrong or expired, we will show a message.

public ActionResult Dashboard()
{
    if (HttpContext.Session.GetString("IsValidTwoFactorAuthentication") == "true")
    {
        return View();
    }
    else
    {
        return RedirectToAction("Login");
    }
}

In the above code, I created a method for the dashboard which will be accessed only when multi-factor authentication is successful.

 public ActionResult Logout()
 {
     HttpContext.Session.Remove("UserName");
     HttpContext.Session.Remove("IsValidTwoFactorAuthentication");
     HttpContext.Session.Remove("UserUniqueKey");
     return RedirectToAction("Login");
 }

In the above code, we have removed data stored in the session for logout.

Step 8. Add Login View

To create a view, right-click on the Login() method, click, 'Add View', and name the view, I named it 'Login.cshtml'. Your view file is created.

In Login. cshtml

<div class="center">
    <div class="row" id="login">
        <div id="login-box" class="col-md-12">
            <h2 class="text-info">Login</h2>
            @using (Html.BeginForm("Verify", "Login", FormMethod.Post))
            {
                <div class="form-group mb-3">
                    <label class="form-label mb-3 required" for="username">Username</label>
                    @Html.TextBoxFor(m => m.Username, new { @class = "form-control", @placeholder = "Username", @required = true })
                </div>
                <div class="form-group mb-3">
                    <label class="form-label mb-3 required" for="password">Password</label>
                    @Html.PasswordFor(m => m.Password, new { @class = "form-control", @placeholder = "Password", @required = true })
                </div>
                <div class="form-group text-center">
                    <input type="submit" name="submit" class="btn btn-success" value="Login">
                </div>
            }
        </div>

        <div>
            @ViewBag.Message
        </div>
    </div>
</div>

In the above code, I have added a form to take username and password. And if there is an error message it will be displayed below.

Step 9. Add Multi-Factor Authentication View

Right-click on the MultiFactorAuthentication() method, click, 'Add View', and name the view, I named it 'MultiFactorAuthentication.cshtml'. Your view file is created.

<div class="center">
    <div class="row" id="login">
        <div id="login-box" class="col-md-12">
            <div class="mb-3">
                <img src="@ViewBag.BarcodeImageUrl" width="300" height="300" />
            </div>
            <div class="row mb-3">
                Manual Setup Code : @ViewBag.SetupCode
            </div>
            <div>
                @using (Html.BeginForm("MultiFactorAuthenticate", "Login", FormMethod.Post))
                {
                    <div class="form-group mb-3">
                        <label class="form-label mb-3 required" for="username">Code</label>
                        <input type="text" class="form-control" placeholder="Enter Authenticator Code" name="CodeDigit" />
                    </div>
                    <div class="form-group text-center">
                        <input type="submit" class="btn btn-success" />
                    </div>
                }
            </div>
        </div>
    </div>
</div>

In the above code, I have shown the QR code that the user will scan in any authenticator app either Google or Microsoft Authenticator. Then there is the Manual setup code. Then I added a form to take the authenticator code.

Here, the user can either use the QR code or the setup code in the authenticator app to get the code.

Step 10. Add Dashboard View

Right-click on the Dashboard() method, click, 'Add View', and name the view, I named it 'Dashboard. cshtml'. Your view file is created.

<div>
    Hey, you have successfully logged in using Multi factor authentication.
</div>

Here, I have shown a demo message to the user that will be shown once, the user completes multi-factor authentication.

Now, you can run the app.

Login

This is the login view. after submitting the form, the next page will open.

Submit

Here, the user will either scan the code or enter the setup code in the authenticator app to get the code. Then the user will submit the code. If the code is valid, the dashboard will be shown.

Code

Now, we have successfully implemented multi-factor authentication in the ASP.NET MVC project.

Conclusion

Implementing multi-factor authentication (MFA) using an authenticator app in an ASP.NET MVC project enhances the security of user authentication. The process involves setting up the project, configuring authentication keys, and creating necessary models and controllers for login and verification. By integrating a two-factor authentication system, users are required to provide an additional verification code generated by an authenticator app, thereby significantly improving protection against unauthorized access.