Build a Secure OTP Authentication System with ASP.NET Core (.NET 10)

In This Article
- Overview
- Architecture & Authentication Flow
- Verify OTP & Login Flow
- Database Models
- ApplicationUser (ASP.NET Identity)
- DTOs (Data Transfer Objects)
- AuthController (ASP.NET Core .NET 10)
- Core Endpoints
- Configuration (ASP.NET Core 10)
- Program.cs (Highlights)
- Email OTP Service
- Security Features
- Testing Guide
- Swagger Testing
- Production Enhancements
- Troubleshooting Tips
- Conclusion
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
Future-Proof Your App with Scalable & Secure Login Systems
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 SolutionOTP not received Check SMTP config & spam folderInvalid OTP Verify expiry & IsUsed flagJWT rejected Check issuer, audience, expiryUser not found Ensure EmailConfirmed = trueDB 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.


