
Ravi Kumar
••4 min read
Build a Secure OTP Authentication System with ASP.NET Core (.NET 10)
In modern applications, passwordless authentication is becoming the preferred choice for both security and user experience. In this blog, we’ll walk through a complete OTP-based authentication system built with ASP.NET Core (.NET 10) that supports both traditional password login and email-based OTP login, powered by ASP.NET Core Identity and JWT tokens.
This implementation is production-ready, scalable, and follows industry best practices.
Overview
This authentication system enables users to log in securely using a One-Time Password (OTP) sent to their email. It also keeps traditional email/password login for backward compatibility.
Key Highlights
- 6-digit OTP generation
- 10-minute OTP expiry
- One-time use (prevents replay attacks)
- JWT-based authentication (60-minute session)
- Role-based authorization support
- Secure HTML email templates
Supported Login Methods
- Email + Password
- Email + OTP (Passwordless Login)
Architecture & Authentication Flow
Send OTP Flow
- User submits email
- System validates the user
- Generates a random 6-digit OTP
- Stores OTP with expiry time in database
- Sends OTP via email
- Returns success response
Verify OTP & Login Flow
- User submits email + OTP
- OTP is validated (correct, unused, not expired)
- OTP is marked as used
- JWT token is generated
- Token and user details are returned
This design ensures high security with minimal friction for users.
Database Models
EmailOtp Entity
public class EmailOtp
{
public int Id { get; set; }
public string UserId { get; set; }
public string Otp { get; set; }
public DateTime ExpiryTime { get; set; }
public bool IsUsed { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
ApplicationUser (ASP.NET Identity)
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public string? PhoneNumber { get; set; }
}
✔ Passwords are hashed automatically by Identity
✔ OTPs are stored with expiry and usage tracking
DTOs (Data Transfer Objects)
DTOs ensure clean validation and secure input handling.
- RegisterDto – User registration
- LoginDto – Email + password login
- SendOtpDto – Request OTP
- VerifyOtpDto – OTP verification
Validation is enforced using DataAnnotations like [Required], [EmailAddress], and [StringLength].
AuthController (ASP.NET Core .NET 10)
The AuthController manages:
- User registration
- Password login
- OTP generation
- OTP verification
- JWT token creation
Core Endpoints
Endpoint Method Description
/api/auth/register POST Register user
/api/auth/login POST Login with password
/api/auth/send-otp POST Send OTP to email
/api/auth/login-with-otp POST Login using OTP
JWT tokens include:
- UserId
- Roles
Configuration (ASP.NET Core 10)
appsettings.json
"JwtSettings": {
"Key": "SuperSecureKeyAtLeast32Characters",
"Issuer": "MsiAPI",
"Audience": "MsiClient"
}
Program.cs (Highlights)
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<MsiDbContext>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true
};
});
✔ Uses JWT Bearer Authentication
✔ Fully compatible with ASP.NET Core 10 minimal hosting model
Email OTP Service
OTP emails are sent using SMTP with HTML templates for a professional look.
OTP Email Features
- Brand header
- Highlighted OTP
- Expiry warning
- Security notice
✔ Uses TLS encryption
✔ Supports Gmail / Outlook SMTP
Security Features
1. OTP Expiry
- Automatically expires after 10 minutes
- Prevents stale OTP usage
2. One-Time Use
- OTP is marked as IsUsed = true after verification
3. JWT Security
- Signed using HMAC SHA256
- Token expires after 60 minutes
- Stateless & scalable
4. Rate Limiting (Recommended)
- Limit OTP requests per email
- Prevent brute-force attacks
5. HTTPS Enforcement
- Protects data in transit
Testing Guide
Tools Supported
- Swagger UI
- Postman
- Unit Tests (xUnit + Moq)
- Integration Tests
- Load Testing (k6)
Swagger Testing
- Run application
- Open /swagger
- Register user
- Send OTP
- Verify OTP
- Authorize with JWT
Production Enhancements
To make this enterprise-ready, consider adding:
- Hash OTPs using BCrypt
- Centralized logging (Serilog / Seq)
- Background service to clean expired OTPs
- Rate limiting middleware
- Redis caching for OTP & sessions
- Monitoring & alerts
Troubleshooting Tips
Issue Solution
OTP not received Check SMTP config & spam folder
Invalid OTP Verify expiry & IsUsed flag
JWT rejected Check issuer, audience, expiry
User not found Ensure EmailConfirmed = true
DB errors Run migrations & check connection
Conclusion
This OTP Authentication System in ASP.NET Core (.NET 10) delivers:
✔ Secure passwordless login
✔ JWT-based session handling
✔ Clean architecture
✔ Production-ready extensibility
It’s ideal for modern web apps, SaaS platforms, and mobile backends that prioritize both security and user experience.
R
Ravi Kumar
Technical writer and software development expert at Murmu Software Infotech, sharing insights on modern web development, software architecture, and best practices.

