A Complete Step-by-Step Guide Using ASP.NET Core and Angular
Security is a critical part of any application. Passwords alone are no longer enough to protect user accounts because many users reuse passwords, pick weak ones, or get exposed through data leaks. Two-Factor Authentication (2FA) adds a powerful second layer of security, making unauthorized access significantly harder.
This article explains how to build a complete Two-Factor Authentication (2FA) system using:
We use simple language and provide full practical implementation.
What You Will Build
By the end of this tutorial, you will have:
A login system that requires both password + 2FA code
A QR code setup process for Google Authenticator
Backend that generates and validates TOTP codes
Database fields for storing 2FA secrets
Angular UI for enabling and verifying 2FA
A secure workflow similar to major applications (Google, GitHub, Microsoft)
Understanding Two-Factor Authentication (2FA)
2FA adds an extra security step after username and password:
User enters email and password
Backend verifies credentials
Backend checks if 2FA is enabled
If enabled → user must enter a 6-digit code
Code changes every 30 seconds
User gets code from an app like Google Authenticator
Backend verifies the code
User is authenticated
This prevents access even if the attacker knows the password.
Choosing the 2FA Method
This tutorial uses TOTP (Time-Based One-Time Password).
It is widely supported by:
Google Authenticator
Authy
Microsoft Authenticator
1Password
LastPass Authenticator
TOTP uses:
This code is valid for 30 seconds.
PART 1: Backend (ASP.NET Core)
Step 1: Install Required Package
We use Otp.NET library for generating TOTP codes.
dotnet add package Otp.NET
Step 2: Update User Model
Models/User.cs
public class User
{
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public bool TwoFactorEnabled { get; set; } = false;
public string TwoFactorSecretKey { get; set; }
}
Step 3: Generate Secret Key for User
When user enables 2FA:
using OtpNet;
public string Generate2FASecretKey()
{
var bytes = KeyGeneration.GenerateRandomKey(20);
return Base32Encoding.ToString(bytes);
}
Step 4: Create Endpoint to Enable 2FA
AuthController.cs
[Authorize]
[HttpPost("enable-2fa")]
public async Task<IActionResult> Enable2FA()
{
var userEmail = User.FindFirstValue(ClaimTypes.Email);
var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == userEmail);
if (user == null)
return Unauthorized();
var secretKey = Generate2FASecretKey();
user.TwoFactorSecretKey = secretKey;
await _context.SaveChangesAsync();
var qrCodeUrl = $"otpauth://totp/{_config["App:Name"]}:{user.Email}?secret={secretKey}&issuer={_config["App:Name"]}&digits=6";
return Ok(new { SecretKey = secretKey, QrCodeUrl = qrCodeUrl });
}
The response returns:
Angular will convert QR code URL into an actual QR image.
Step 5: Validate 2FA Code
[Authorize]
[HttpPost("verify-2fa")]
public async Task<IActionResult> Verify2FA([FromBody] TwoFactorDto dto)
{
var userEmail = User.FindFirstValue(ClaimTypes.Email);
var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == userEmail);
if (user == null)
return Unauthorized();
var totp = new Totp(Base32Encoding.ToBytes(user.TwoFactorSecretKey));
var isValid = totp.VerifyTotp(dto.Code, out _);
if (!isValid)
return BadRequest(new { Message = "Invalid 2FA Code" });
user.TwoFactorEnabled = true;
await _context.SaveChangesAsync();
return Ok(new { Message = "2FA Enabled Successfully" });
}
Step 6: Modify Login Endpoint to Require 2FA
if (user.TwoFactorEnabled)
{
return Ok(new { Require2FA = true, Email = user.Email });
}
Step 7: Create Verify Login 2FA Endpoint
[HttpPost("login-2fa")]
public IActionResult Login2FA(VerifyLoginDto dto)
{
var user = _context.Users.FirstOrDefault(x => x.Email == dto.Email);
if (user == null) return BadRequest("Invalid attempt");
var totp = new Totp(Base32Encoding.ToBytes(user.TwoFactorSecretKey));
if (!totp.VerifyTotp(dto.Code, out _))
return BadRequest("Invalid 2FA code");
var token = _jwt.GenerateToken(user.Email);
return Ok(new { Token = token });
}
Backend is ready.
PART 2: Frontend (Angular)
Step 1: Install QR Code Library
npm install angularx-qrcode
Step 2: Create Enable 2FA Component
ng generate component enable-2fa
Template
<h2>Enable Two-Factor Authentication</h2>
<button (click)="enable()">Generate QR Code</button>
<div *ngIf="qrCodeUrl">
<qrcode [qrdata]="qrCodeUrl" [width]="200"></qrcode>
<p>Secret Key: {{ secretKey }}</p>
</div>
<input type="text" placeholder="Enter 6 digit code" [(ngModel)]="code">
<button (click)="verify()">Verify</button>
<p>{{ message }}</p>
Component Script
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
templateUrl: './enable-2fa.component.html'
})
export class Enable2FAComponent {
qrCodeUrl: string = "";
secretKey: string = "";
code: string = "";
message: string = "";
constructor(private http: HttpClient) {}
enable() {
this.http.post<any>("https://localhost:5001/api/auth/enable-2fa", {})
.subscribe(res => {
this.qrCodeUrl = res.qrCodeUrl;
this.secretKey = res.secretKey;
});
}
verify() {
this.http.post<any>("https://localhost:5001/api/auth/verify-2fa", { code: this.code })
.subscribe({
next: res => this.message = res.message,
error: err => this.message = "Invalid 2FA code"
});
}
}
Step 3: Modify Login Component
submit() {
this.auth.login(this.model).subscribe(res => {
if (res.require2FA) {
this.needs2FA = true;
this.emailFor2FA = res.email;
} else {
this.saveToken(res.token);
}
});
}
Step 4: Add Verify Login 2FA Step
verify2FA() {
this.http.post<any>(this.apiUrl + "/login-2fa", {
email: this.emailFor2FA,
code: this.code
})
.subscribe(res => {
this.auth.saveToken(res.token);
this.router.navigate(['/dashboard']);
});
}
Part 3: Complete Flow
The authentication process becomes:
Registration:
User registers
User logs in
User visits Enable 2FA page
User scans QR code
User enters verification code
2FA is activated
Login
User enters email + password
Backend checks if 2FA is enabled
If enabled → Angular asks for the verification code
User enters 6-digit TOTP
Backend validates code
JWT token is issued
User gets access
Security Benefits of 2FA
Two-Factor Authentication protects against:
Even if an attacker knows the password, they still cannot log in without the 2FA device.
Possible Extensions
I can help you add:
Just tell me what you want.
Conclusion
You now have a complete Two-Factor Authentication (2FA) system using:
This gives your application strong, professional-grade security.