This episode covers NestJS API security: security fundamentals, JWT authentication and guards, Passport integration with auth strategies, and role-based access control and role-based permissions.

An API without authentication is an open door. Episode 12 builds the first security layer that's mandatory: JWT authentication, Passport integration, and role-based authorization. You'll learn to protect endpoints so only authenticated users with the right role can access them.
Before diving into code, understand the underlying security principles:
NestJS provides a clean structure for applying all of these principles through guards and strategies.
npm install bcrypt @nestjs/jwtimport * as bcrypt from "bcrypt";
export async function hashPassword(password: string): Promise<string> {
const salt = await bcrypt.genSalt(10);
return bcrypt.hash(password, salt);
}bcrypt.hash produces a secure hash with a random salt — install it first with npm install bcrypt @nestjs/jwt. Never store raw passwords in the database.
A JSON Web Token (JWT) is a token that carries encrypted claims — usually the user's identity and expiry time. This token is signed by the server, so it can be verified without storing a session on the server.
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
async login(user: { id: number; username: string }): Promise<{ accessToken: string }> {
const payload = { sub: user.id, username: user.username };
return {
accessToken: await this.jwtService.signAsync(payload),
};
}
}signAsync creates a token containing the user payload. This token is sent to the client and used on subsequent requests.
A guard determines whether a request may proceed. An authentication guard checks the token on the request, while an authorization guard checks the user's role.
The guard reads the token from the Authorization header, verifies it with JwtService, then stores the payload in request.user:
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private readonly jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();
const auth = request.headers.authorization;
const token = auth?.startsWith("Bearer ") ? auth.split(" ")[1] : undefined;
if (!token) {
throw new UnauthorizedException("Token tidak ditemukan");
}
try {
request.user = await this.jwtService.verifyAsync(token);
} catch {
throw new UnauthorizedException("Token tidak valid");
}
return true;
}
}The guard validates the token from the Authorization header and stores the payload in request.user.
NestJS also supports Passport — a popular authentication library. With @nestjs/passport and passport-jwt:
npm install @nestjs/passport passport passport-jwt
npm install -D @types/passport-jwt@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET ?? "rahasia",
});
}
async validate(payload: { sub: number; username: string }) {
return { userId: payload.sub, username: payload.username };
}
}To restrict access per role, create a custom decorator together with the guard that reads it:
export const ROLES_KEY = "roles";
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}@Controller("admin")
@UseGuards(RolesGuard)
export class AdminController {
@Get("reports")
@Roles("admin")
getReports(): string {
return "Laporan rahasia";
}
}Episode 12 builds a solid security foundation. Key takeaways:
request.user.@Roles and RolesGuard apply role-based authorization.Reflector to read decorator metadata.