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
User registration system
Backend generates a unique verification token
Email is sent using HTML templates with a verification link
User clicks the verification link
Backend verifies the token and marks the user as verified
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:
Rich content like logos, buttons, and colors
Clear call-to-action for verification
Better mobile compatibility
Branding consistency
Improved user engagement
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
Register a new user → check email inbox
Click verification link → Angular page displays success
Attempt login before verification → backend should prevent login
Attempt login after verification → login succeeds
Part 5: Optional Enhancements
Add token expiration for verification links
Send resend verification email feature
Use dynamic HTML templates with Razor or Liquid
Add email tracking (opened, clicked)
Style emails for mobile responsiveness
Include company branding and logos
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.