
Building a Production-Ready Auth Module in NestJS with Prisma and MySQL
Building a Complete Enterprise-Grade Auth Module in NestJS
Authentication is not just about login and register.
A production system requires secure token management, OTP verification,
password reset flows, role-based access control, and strong validation.
This guide walks through building a complete enterprise-ready Auth module using:
- NestJS
- Prisma ORM
- MySQL
- JWT (Access + Refresh Tokens)
- OTP verification
- RBAC (Role-Based Access Control)
1. Architecture Overview
A clean and scalable structure:
modules/auth/
├─ controllers/
├─ services/
├─ dtos/
├─ guards/
├─ strategies/
└─ entities/
Responsibilities:
- Controllers → HTTP layer
- Services → Business logic
- DTOs → Validation
- Guards → Authorization
- Strategies → JWT validation
- Prisma → Database layer
2. Prisma User Model
model User {
id Int @id @default(autoincrement())
username String
email String @unique
password String
role String @default("USER")
refreshToken String?
otpHash String?
otpExpiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Key points:
- Passwords are hashed
- Refresh tokens stored hashed
- OTP stored hashed
- Role field enables RBAC
3. Core Features
User Registration
- Validate DTO
- Hash password (bcrypt)
- Store user
- Optional email verification
Login
- Validate credentials
- Generate Access Token (15m)
- Generate Refresh Token (7d)
- Store hashed refresh token
Refresh Token Rotation
- Validate stored refresh token
- Issue new access token
- Replace refresh token
4. JWT Strategy
Access Token:
- Short lived (15 minutes)
- Used for protected routes
Refresh Token:
- Long lived (7 days)
- Stored hashed in DB
- Rotated on refresh
Environment variables:
JWT_SECRET=access_secret
JWT_REFRESH_SECRET=refresh_secret
5. OTP-Based Password Reset
Forgot Password
- Generate 6-digit OTP
- Hash OTP before storing
- Set expiry (10 minutes)
- Send via email
Reset Password
- Validate OTP
- Check expiration
- Hash new password
- Clear OTP fields
Security:
- Limit OTP attempts
- Never reveal if email exists
- Log reset attempts
6. Change Password
- Require JWT authentication
- Verify old password
- Hash new password
- Invalidate existing refresh tokens
7. Update Profile
Protected route.
User can update:
- Username
- Avatar (optional)
Always validate input using DTOs.
8. Role-Based Access Control (RBAC)
Create RolesGuard:
@Roles('ADMIN')
@Get('admin-only')
Guard checks:
- JWT validity
- User role
- Permission access
9. DTO Validation Example
export class RegisterDto {
@IsString()
username: string;
@IsEmail()
email: string;
@IsStrongPassword()
password: string;
}
Enable global validation:
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
10. Security Best Practices
- Hash passwords and tokens
- Enable HTTPS
- Use rate limiting
- Implement refresh token rotation
- Log auth events
- Use strong password validation
- Avoid detailed error messages
- Keep secrets in environment variables
11. Clean Auth Flow Summary
Register → Hash password → Store user
Login → Validate → Issue tokens
Access Protected Route → JWT Guard
Refresh → Rotate token
Forgot Password → OTP → Reset
RBAC → Role-based Guards
Final Thoughts
An enterprise Auth module must be:
- Secure
- Modular
- Scalable
- Maintainable
NestJS combined with Prisma provides strong typing, clean architecture, and powerful extensibility for real-world applications.
Authentication is not just a feature --- it is infrastructure.