Introduction
Authentication is a critical aspect of any web application. In this article, we will build a production-ready authentication system using NestJS, Prisma, JWT, and Nodemailer. We'll cover user registration, email verification, password reset, authentication logging, and security best practices.
By the end, youβll have a solid NestJS authentication boilerplate that can be used in real-world applications. The complete source code is available on GitHub:
π nestjs-auth GitHub Repository
π― Why NestJS for Authentication?
NestJS is a progressive Node.js framework that makes backend development structured, scalable, and maintainable. Hereβs why NestJS is a great choice for authentication:
β
Modular architecture β Organizes authentication, user management, and security as separate modules.
β
TypeScript support β Provides strong typing and cleaner code.
β
Built-in decorators β Simplifies authentication, validation, and guards.
β
Scalability β Ideal for building large applications with microservices support.
π Tech Stack & Features
π Stack
- NestJS β Backend framework
- Prisma β ORM for MySQL
- JWT (JSON Web Tokens) β Authentication
- Nodemailer β Email service
- Helmet.js β Security headers
- Rate Limiting β Prevent brute force attacks
π₯ Key Features
β
User Authentication (JWT) β Register, login, logout
β
Email Verification (via OTP)
β
Password Reset (via OTP)
β
Role-Based Access Control (RBAC)
β
Request Throttling for Security
β
Logging Authentication Events (IP, status, user info, etc.)
β
Swagger API Documentation
π¦ Project Setup
1οΈβ£ Clone the Repository
git clone https://github.com/jaleeldgk/nestjs-auth.git
cd nestjs-auth
2οΈβ£ Install Dependencies
npm install
3οΈβ£ Set Up Environment Variables
Rename .env.example
to .env
and update the values:
DATABASE_URL="mysql://user:password@localhost:3306/nest_auth"
JWT_SECRET="your_jwt_secret"
EMAIL_USER="your_smtp_email"
EMAIL_PASS="your_smtp_password"
4οΈβ£ Set Up Database (MySQL with Prisma)
npx prisma migrate dev --name init
npx prisma generate
5οΈβ£ Run the Server
npm run start:dev
The API will be available at
http://localhost:3000
π Authentication Flow
1οΈβ£ User Registration & Email Verification
When a user registers, an OTP is sent via email. The user must verify their email before logging in.
Prisma User Model (prisma/schema.prisma
)
model User {
id String @id @default(uuid())
email String @unique
password String
name String
isVerified Boolean @default(false)
emailVerifiedAt DateTime? @default(null)
createdAt DateTime @default(now())
}
Register & Send OTP (auth.service.ts
)
async register(dto: AuthDto) {
const hashedPassword = await bcrypt.hash(dto.password, 10);
const user = await this.prisma.user.create({
data: { email: dto.email, name: dto.name, password: hashedPassword },
});
// Generate OTP
const otp = Math.floor(100000 + Math.random() * 900000).toString();
await this.prisma.otp.create({
data: { userId: user.id, otp, expiresAt: new Date(Date.now() + 10 * 60 * 1000) },
});
await this.emailService.sendOTP(user.email, otp);
return { message: 'OTP sent to email' };
}
2οΈβ£ User Login & JWT Authentication
After successful email verification, the user can log in and receive a JWT token.
Login API (auth.service.ts
)
async signin(dto: AuthDto) {
const user = await this.prisma.user.findUnique({ where: { email: dto.email } });
if (!user || !(await bcrypt.compare(dto.password, user.password))) {
throw new UnauthorizedException('Invalid credentials');
}
if (!user.isVerified) {
throw new ForbiddenException('Email not verified');
}
const token = this.jwtService.sign({ id: user.id, role: user.role });
return { accessToken: token };
}
3οΈβ£ Password Reset (OTP Based)
- User requests a password reset.
- A one-time password (OTP) is sent via email.
- The user enters the OTP and sets a new password.
Generate & Send OTP (auth.service.ts
)
async requestPasswordReset(email: string) {
const user = await this.prisma.user.findUnique({ where: { email } });
if (!user) throw new NotFoundException('User not found');
const otp = Math.floor(100000 + Math.random() * 900000).toString();
await this.prisma.otp.create({
data: { userId: user.id, otp, expiresAt: new Date(Date.now() + 10 * 60 * 1000) },
});
await this.emailService.sendOTP(user.email, otp);
return { message: 'OTP sent to email' };
}
π‘ Security Enhancements
πΉ Rate Limiting (Throttle)
To prevent brute force attacks, we add rate limiting using @nestjs/throttler
.
import { Throttle } from '@nestjs/throttler';
@Throttle(5, 60) // Allow 5 requests per minute per IP
async signin(@Body() dto: AuthDto) {
return this.authService.signin(dto);
}
πΉ Helmet for Security Headers
import helmet from 'helmet';
app.use(helmet()); // Adds security headers
π API Documentation (Swagger)
We use Swagger for API documentation.
Access it at:
π http://localhost:3000/api/docs
Enable Swagger in main.ts
const config = new DocumentBuilder()
.setTitle('NestJS Auth API')
.setDescription('Authentication system with JWT, OTP, and Email Verification')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
π Future Upgrades
- β Winston Logging for Authentication Events
- β Improved RBAC with Permissions
- β Docker Support for Deployment
- β Multi-Tenant Authentication
- β Two-Factor Authentication (2FA)
π‘ Conclusion
In this guide, we built a secure authentication system with NestJS featuring JWT authentication, email verification, password reset, and security best practices. The project is modular, scalable, and production-ready.
π Ready to use it? Check out the source code:
π GitHub Repository
π¬ Whatβs Next?
Are there any features you'd like to see? Let me know in the comments! π
Top comments (0)