Welcome back to the NestJS Expert Series! π
In Part https://dev.to/devto_with_yog/nestjs-expert-series-part-2-database-integration-with-prisma-typeorm-ica, we integrated our NestJS app with a database using Prisma and TypeORM.
Now itβs time to secure our application with Authentication (who you are) and Authorization (what you can do).
In this article, weβll build a JWT-based authentication system and use Guards to implement role-based access control.
β‘ Why JWT for Authentication?
JWT (JSON Web Token) is a compact, URL-safe token format widely used for stateless authentication.
β
Stateless β No need to store session data on the server.
β
Scalable β Works well in distributed systems.
β
Cross-platform β Can be used by web, mobile, and microservices.
π¦ Step 1: Install Required Packages
π οΈ Step 2: Auth Module Setup
Generate an auth module, service, and controller:
nest g module auth
nest g service auth
nest g controller auth
π€ Step 3: User Entity (with Prisma Example)
If youβre using Prisma, update your schema:
model User {
id Int @id @default(autoincrement())
email String @unique
password String
role String @default("user")
}
Run migration:
npx prisma migrate dev --name add_user_model
π Step 4: Auth Service (Register & Login)
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
async hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 10);
}
async validatePassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
async generateToken(user: { id: number; email: string; role: string }) {
return this.jwtService.sign({ sub: user.id, email: user.email, role: user.role });
}
}
π Step 5: Auth Controller
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Post('register')
async register(@Body() body: { email: string; password: string }) {
const hashed = await this.authService.hashPassword(body.password);
// Save user to DB with hashed password
return { message: 'User registered successfully' };
}
@Post('login')
async login(@Body() body: { email: string; password: string }) {
// Find user in DB and validate password
// If valid, generate token
return { access_token: await this.authService.generateToken({ id: 1, email: body.email, role: 'user' }) };
}
}
π‘οΈ Step 6: Guards for Authorization
NestJS Guards decide whether a request should proceed or not.
Create a roles.guard.ts:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
if (!requiredRoles) return true;
const { user } = context.switchToHttp().getRequest();
return requiredRoles.includes(user.role);
}
}
Use a custom decorator roles.decorator.ts:
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
Apply it in a controller:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { Roles } from './roles.decorator';
import { RolesGuard } from './roles.guard';
@Controller('admin')
export class AdminController {
@Get()
@Roles('admin')
@UseGuards(RolesGuard)
findAdminData() {
return { secret: 'This is admin-only data' };
}
}
π Conclusion
β We built JWT-based authentication.
β We secured routes with Guards and Role-based Authorization.
β We now have the foundation for a secure, multi-user NestJS application.
In the next part, weβll tackle Validation & Error Handling with Pipes and Filters to make our APIs more robust.
π‘ If you found this helpful, drop a β€οΈ on Dev.to and follow me for Part 4 of the NestJS Expert Series.
Top comments (0)