A Complete Step-by-Step Tutorial for Beginners

Email verification is an essential step in modern applications. It ensures that users provide valid email addresses, reduces spam registrations, and improves security. While sending plain text emails is simple, using HTML email templates enhances user experience by allowing styled content, logos, buttons, and better formatting.

In this tutorial, we will create a full email verification system using:

We will explain each step clearly with practical examples.

What You Will Build

  1. User registration system

  2. Backend generates a unique verification token

  3. Email is sent using HTML templates with a verification link

  4. User clicks the verification link

  5. Backend verifies the token and marks the user as verified

  6. Angular frontend displays success or error message

This workflow is professional and used in production applications like Gmail, Microsoft, and online banking.

Why HTML Email Templates Are Important

Using HTML templates allows:

Part 1: Backend Implementation (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; }
}

Step 2: Generate Verification Token

Create a helper class:

using System.Security.Cryptography;

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

Step 3: Create Email Service Using HTML Templates

Install MailKit and MimeKit for sending HTML emails:

dotnet add package MailKit

File: Services/EmailService.cs

using MailKit.Net.Smtp;
using MimeKit;

public class EmailService
{
    private readonly IConfiguration _config;

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

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

        // Load HTML template
        var template = File.ReadAllText("EmailTemplates/VerifyEmailTemplate.html");
        template = template.Replace("{{UserName}}", userName)
                           .Replace("{{VerifyUrl}}", verifyUrl);

        var email = new MimeMessage();
        email.From.Add(MailboxAddress.Parse(_config["Email:From"]));
        email.To.Add(MailboxAddress.Parse(toEmail));
        email.Subject = "Verify Your Email";

        var bodyBuilder = new BodyBuilder
        {
            HtmlBody = template
        };
        email.Body = bodyBuilder.ToMessageBody();

        using var smtp = new SmtpClient();
        await smtp.ConnectAsync(_config["Email:SmtpServer"], int.Parse(_config["Email:Port"]), true);
        await smtp.AuthenticateAsync(_config["Email:Username"], _config["Email:Password"]);
        await smtp.SendAsync(email);
        await smtp.DisconnectAsync(true);
    }
}

Step 4: Create HTML Template

Folder: EmailTemplates/VerifyEmailTemplate.html

<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: Arial, sans-serif; background-color: #f5f5f5; }
        .container { max-width: 600px; margin: auto; background: white; padding: 20px; border-radius: 5px; }
        .button { background-color: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; }
        .footer { margin-top: 20px; font-size: 12px; color: #888; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Hello {{UserName}},</h2>
        <p>Thank you for registering. Please click the button below to verify your email address:</p>
        <p><a href="{{VerifyUrl}}" class="button">Verify Email</a></p>
        <p>If you did not register, please ignore this email.</p>
        <div class="footer">
            © 2025 MyApp. All rights reserved.
        </div>
    </div>
</body>
</html>

Step 5: Registration Endpoint

AuthController.cs

[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.FullName, user.VerificationToken);

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

Step 6: Verify Email Endpoint

[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;
    user.VerificationToken = null;
    await _context.SaveChangesAsync();

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

Part 2: Frontend Implementation (Angular)

Angular is optional but recommended for user-friendly verification pages.

Step 1: Create VerifyEmail Component

ng generate component verify-email

verify-email.component.ts

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

@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>
<a routerLink="/login">Go to Login</a>

Step 2: Add Route

app-routing.module.ts

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

Part 3: Configuration

appsettings.json

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

Part 4: Testing the system

  1. Register a new user → check email inbox

  2. Click verification link → Angular page displays success

  3. Attempt login before verification → backend should prevent login

  4. Attempt login after verification → login succeeds

Part 5: Optional Enhancements

Conclusion

You now have a complete Email Verification System with HTML templates using:

This approach ensures professional, secure, and user-friendly registration workflows suitable for production applications.