ASP.NET Core  

Building an Email Verification System in ASP.NET Core and Angular

User registration is a core feature of most modern applications. But registration alone is not enough. To avoid fake accounts, spam, and unauthorized access, applications must verify user email addresses before allowing access to protected features. Email verification adds a strong security layer and improves user trust in your system.

In this article, you will learn how to build a complete Email Verification System using:

  • ASP.NET Core Web API

  • SQL Server

  • Angular (Frontend)

  • Token-based verification links

  • Email sending using SMTP

This tutorial uses simple language, clear structure, and practical code so beginners and professionals can implement it easily.

What You Will Build

By the end of this tutorial, you will have:

  1. A registration system that sends a verification email

  2. A unique verification token stored in the database

  3. A verification URL sent to the user

  4. An API endpoint to verify the email

  5. Angular pages for registration and email confirmation

  6. A secure flow that ensures the user cannot log in unless verified

This is the same pattern used by most professional systems such as Google, Microsoft, Amazon, and major SaaS platforms.

Why Email Verification Matters

Email verification protects the system from:

  • Fake accounts

  • Abuse of free plans

  • Unauthorized access

  • Spam signups

  • Compromised login attempts

It ensures the email belongs to the user who signed up.

System Architecture Overview

The verification system works like this:

  1. User registers in Angular

  2. Backend creates a user record in SQL

  3. Backend generates a unique verification token

  4. Token saved to database

  5. Email sent to user with verification link

  6. User clicks verification link

  7. Backend confirms the token

  8. User is marked as EmailVerified = true

  9. User can now log in

This is a secure and scalable method used in industry.

Part 1: Backend Iplementation (ASP.NET Core)

Step 1: Update User Model

File: 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 EmailVerified { get; set; } = false;
    public string VerificationToken { get; set; }
    public DateTime? VerifiedAt { get; set; }
}

We added:

  • EmailVerified

  • VerificationToken

  • VerifiedAt

Step 2: Generate Verification Token

Create a helper:

public static class TokenGenerator
{
    public static string GenerateToken()
    {
        return Convert.ToHexString(RandomNumberGenerator.GetBytes(32));
    }
}

Step 3: Modify Registration Endpoint

When user registers:

  • Create verification token

  • Save it

  • Send verification email

[HttpPost("register")]
public async Task<IActionResult> Register(UserDto request)
{
    if (_context.Users.Any(u => u.Email == request.Email))
        return BadRequest(new { Message = "Email already exists" });

    var user = new User
    {
        FullName = request.FullName,
        Email = request.Email,
        PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password),
        VerificationToken = TokenGenerator.GenerateToken()
    };

    _context.Users.Add(user);
    await _context.SaveChangesAsync();

    await _emailService.SendVerificationEmail(user.Email, user.VerificationToken);

    return Ok(new { Message = "Registration successful. Please check your email to verify your account." });
}

Step 4: Configure Email Sending (SMTP)

Create Services/EmailService.cs

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

public class EmailService
{
    private readonly IConfiguration _config;

    public EmailService(IConfiguration config)
    {
        _config = config;
    }

    public async Task SendVerificationEmail(string email, string token)
    {
        var verifyUrl = $"{_config["App:BaseUrl"]}/verify-email?token={token}";

        var message = new MailMessage();
        message.From = new MailAddress(_config["Email:From"]);
        message.To.Add(email);
        message.Subject = "Verify Your Email";
        message.Body = $"Click the link to verify your email: {verifyUrl}";

        using var smtp = new SmtpClient(_config["Email:SmtpServer"])
        {
            Port = int.Parse(_config["Email:Port"]),
            Credentials = new NetworkCredential(
                _config["Email:Username"],
                _config["Email:Password"]
            ),
            EnableSsl = true
        };

        await smtp.SendMailAsync(message);
    }
}

Add to Program.cs:

builder.Services.AddScoped<EmailService>();

Step 5: Add Email Settings in appsettings.json

"Email": {
  "From": "[email protected]",
  "SmtpServer": "smtp.gmail.com",
  "Port": "587",
  "Username": "[email protected]",
  "Password": "your-email-password"
},
"App": {
  "BaseUrl": "http://localhost:4200"
}

Step 6: Create Verify Email API Endpoint

File: AuthController.cs

[HttpGet("verify-email")]
public async Task<IActionResult> VerifyEmail(string token)
{
    var user = await _context.Users.FirstOrDefaultAsync(u => u.VerificationToken == token);
    if (user == null)
        return BadRequest(new { Message = "Invalid token" });

    user.EmailVerified = true;
    user.VerifiedAt = DateTime.Now;
    await _context.SaveChangesAsync();

    return Ok(new { Message = "Email verified successfully" });
}

Step 7: Block Login if Email Not Verified

Modify Login endpoint:

if (!user.EmailVerified)
    return BadRequest(new { Message = "Email not verified. Please check your inbox." });

Backend is now ready.

Part 2: Frontend Implementation (Angular)

Step 1: Update Register Component

After registration, show a message:

this.auth.register(this.model).subscribe({
  next: (res: any) => {
    this.message = "Registered successfully. Check your email to verify your account.";
  }
});

Step 2: Create VerifyEmail Component

ng generate component verify-email

verify-email.component.ts

import { ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';

@Component({
  templateUrl: './verify-email.component.html'
})
export class VerifyEmailComponent {

  message = "";

  constructor(private route: ActivatedRoute, private http: HttpClient) {}

  ngOnInit() {
    const token = this.route.snapshot.queryParams["token"];

    this.http.get("https://localhost:5001/api/auth/verify-email?token=" + token)
      .subscribe({
        next: (res: any) => this.message = res.message,
        error: () => this.message = "Verification failed"
      });
  }
}

verify-email.component.html

<h2>Email Verification</h2>
<p>{{ message }}</p>

Step 3: Add Route

app-routing.module.ts

{ path: 'verify-email', component: VerifyEmailComponent }

Step 4: Modify Login Component

When email is not verified:

error: err => {
  this.message = err.error.message;
}

Part 3: Full Verification flow

  1. User registers

  2. Backend creates a verification token

  3. Email with verification link is sent

  4. User clicks the link

  5. Backend verifies the token

  6. User marked as verified

  7. User can now log in

  8. Angular prevents login until verification is complete

This ensures only real users access your system.

Part 4: Bonus Improvements

You can enhance the system with:

  1. Token expiration for verification links

  2. Resend verification email

  3. Verification email templates (HTML templates)

  4. Tracking verification attempts

  5. Logging email delivery

  6. Adding CAPTCHA to registration

  7. Temporary user accounts before verification

If you need any of these, I can write a full article.

Conclusion

In this article, you learned how to create a complete Email Verification System using ASP.NET Core, SQL Server, and Angular. The system includes:

  • Secure registration

  • Token-based verification

  • Email sending via SMTP

  • Protected login until verification

  • Angular verification page

  • Database tracking of verified users

This is a professional and secure approach used in production applications.