๐Ÿ”Authentication & Authorization

Secure your app using JWT-based authentication and granular role-based access control.

๐Ÿชช JWT Authentication

Welcome to the world of JWT Authentication! In this chapter, you'll learn how to implement secure login and signup flows using Passport and NestJS AuthModule. We'll cover everything from token management to protecting routes with guards.

๐Ÿ’ก What is JWT?

JWT (JSON Web Tokens) are a compact, URL-safe means of representing claims to be transferred between parties in JSON format. They're widely used for authentication and information exchange.

๐Ÿ’ก Setting Up JWT Authentication

  • Install required dependencies: @nestjs/passport, passport-jwt, and dotenv.
  • Configure your authentication module with Passport strategies.
  • Implement login and signup endpoints.
import { Module } from '@nestjs/common';
import { AuthModule as NestAuthModule } from '@nestjs/auth';

@Module({
  imports: [NestAuthModule.forRoot({
    secret: 'your-secret-key',
    signOptions: { expiresIn: '1d' }
  })],
})
export class AuthModule {};

๐Ÿ’ก The Strategy Pattern

NestJS uses the strategy pattern to handle different authentication mechanisms. Each strategy is a class that implements a common interface.

import { Strategy } from 'passport-jwt';

export const jwtStrategy = new Strategy(
  {
    secret: 'your-secret-key',
    passReqToCallback: false,
  },
  async (payload, done) => {
    // Validate and return user
    done(null, payload);
  }
);

๐Ÿ’ก Token Management Best Practices

  • Always use HTTPS to transmit tokens.
  • Never store tokens in local storage - use HTTP-only cookies instead.
  • Implement token expiration and refresh mechanisms.
  • Use strong secret keys for signing tokens.

๐Ÿ’ก Protecting Routes with Guards

Guards are a core feature of NestJS that allow you to control access to your application's endpoints. They can be used to check if a user is authenticated before allowing access to a route.

import { CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

export class AuthGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const isPublic = this.reflector.get('isPublic', context.getHandler());
    return !isPublic;
  }
}

โŒ Common Mistakes to Avoid

  • Don't expose your secret key in the client code.
  • Avoid using local storage for storing tokens - it's insecure!
  • Never return sensitive user data in the token payload.
  • Don't forget to implement token expiration and refresh mechanisms.

โœ… Best Practices for Secure Authentication

  • Use HTTPS at all times.
  • Implement CSRF protection for frontend applications.
  • Store tokens in HTTP-only cookies or secure storage.
  • Validate and sanitize all user inputs.
  • Rotate your secret keys periodically.

๐Ÿง‘โ€โš–๏ธ Role-based Access Control & Guards

Welcome to our comprehensive guide on Role-based Access Control (RBAC) and Guards in NestJS! In this chapter, we'll explore how to build robust access control systems using custom decorators, metadata reflection, and guards. You'll learn how to implement both role-based and permission-based patterns to protect sensitive endpoints while maintaining dynamic scoping based on authenticated users.

๐Ÿ’ก Key Concepts: RBAC Basics ๏ฟฝ shields

Before diving into implementation, let's understand the core concepts of RBAC:

  • Role-based Access Control (RBAC): A security model where access rights are granted based on roles assigned to users.
  • Roles: Defined groups of permissions (e.g., 'admin', 'user', 'guest').
  • Permissions: Fine-grained actions (e.g., 'read', 'write', 'delete') that define what a role can do.

๐Ÿ’ก Implementing Guards in NestJS ๐Ÿ›ก๏ธ

Guards are middleware-like functions that intercept incoming requests and determine if access should be granted or denied.

import { CanActivate, ExecutionContext } from '@nestjs/common';

export class RoleGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    
    // Example logic: Check if user has required role
    const userRoles = request.user.roles;
    
    return userRoles.includes('admin');
  }
}

โœ… Using Custom Decorators ๐ŸŽฎ

NestJS allows us to create custom decorators using metadata reflection. This enables us to define roles and permissions directly on our controller methods.

@UseGuards(RoleGuard)
@SetMetadata('roles', ['admin'])
export class UserController {
  @Get()
  findAll(@CurrentUser() user: User) {
    return user;
  }
}

๐Ÿ’ก Best Practices ๐ŸŒŸ

  • Keep permissions granular to avoid over-permissioning.
  • Use the principle of least privilege โ€“ grant minimal access required.
  • Regularly audit and update role definitions.

โœ… Advanced Patterns: Permission Scoping ๐Ÿงฉ

Scoping allows you to dynamically control access based on context-specific data, such as ownership or project membership.

@UseGuards(OwnershipGuard)
export class PostController {
  @Get(':id')
  findOne(@Param('id') id: string, @CurrentUser() user: User) {
    return this.postsService.findOne(id);
  }
}

// OwnershipGuard implementation
if (!post.ownerId.equals(user.id)) {
  throw new ForbiddenException('You are not the owner');
}

โŒ Common Mistakes to Avoid ๐Ÿšซ

  • Avoid hardcoding roles in guards โ€“ use metadata instead.
  • Don't mix role-based and permission-based logic without clear separation.
  • Never assume user context โ€“ always verify authentication before authorization.

๐Ÿ’ก Real-World Application: Building an RBAC System ๐Ÿข๏ธ

Let's outline steps to build a production-ready RBAC system:

  • 1. Define core roles and permissions.
  • 2. Create role-permission mappings.
  • 3. Implement guards for different access levels.
  • 4. Use decorators to apply authorization policies.
  • 5. Set up request-scoped services for dynamic checks.

๐Ÿ’ก Troubleshooting & Debugging ๐Ÿ› ๏ธ

Common issues and how to resolve them:

  • Guards not triggering: Check decorator order and metadata setup.
  • Role checking fails: Verify user context is properly passed through middleware.
  • Permission scoping issues: Ensure scoped data is correctly queried using user context.

Quiz

Question 1 of 11

Which of the following is a best practice for storing JWT tokens?

  • Local Storage
  • Session Storage
  • HTTP-only cookies
  • All of the above