Learn NestJS - Security & Authentication
Episode 12 of 24

Learn NestJS - Security & Authentication

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.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

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.

NestJS Security Fundamentals

Basic Principles

Before diving into code, understand the underlying security principles:

  • Never trust input — always validate.
  • Never store plain-text passwords — use a hash.
  • Apply least privilege — give the minimal access required.
  • Always use HTTPS in production.

NestJS provides a clean structure for applying all of these principles through guards and strategies.

Hashing Passwords with bcrypt

Install bcrypt dan @nestjs/jwt
npm install bcrypt @nestjs/jwt
JSMeng-hash password dengan bcrypt
import * 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.

JWT Authentication

What is JWT

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.

Creating a Token in AuthService

JSAuthService untuk sign JWT
@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.

Guards and Passport Integration

What is a Guard

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.

Creating a JwtAuthGuard

The guard reads the token from the Authorization header, verifies it with JwtService, then stores the payload in request.user:

JSGuard autentikasi JWT
@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.

Passport Strategy

NestJS also supports Passport — a popular authentication library. With @nestjs/passport and passport-jwt:

Install Passport JWT
npm install @nestjs/passport passport passport-jwt
npm install -D @types/passport-jwt
JSPassport JWT strategy
@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 };
  }
}

Role-Based Access Control

Creating a Roles Decorator and RolesGuard

To restrict access per role, create a custom decorator together with the guard that reads it:

JSDekorator @Roles dan RolesGuard
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));
  }
}

Using Roles in a Controller

JSEndpoint yang dilindungi role
@Controller("admin")
@UseGuards(RolesGuard)
export class AdminController {
  @Get("reports")
  @Roles("admin")
  getReports(): string {
    return "Laporan rahasia";
  }
}

Conclusion

Episode 12 builds a solid security foundation. Key takeaways:

  • Passwords are always hashed with bcrypt, never stored in plain text.
  • JWTs carry user claims and are signed by the server.
  • Guards validate tokens and store the user in request.user.
  • @Roles and RolesGuard apply role-based authorization.
  • Use Reflector to read decorator metadata.
Learn NestJS - Security & Authentication | Learning NestJS