Skip to content

Authentication Decorators

Overview

The Authentication module provides custom decorators that simplify route protection and role-based access control in the BidScript backend. These decorators work with guards to provide a clean and declarative way to secure endpoints.

Public Decorator

Overview

The @Public() decorator marks routes as publicly accessible, exempting them from the global JWT authentication guard.

Source: src/auth/decorators/public.decorator.ts

import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

Usage

import { Public } from './auth/decorators/public.decorator';

@Controller('auth')
export class AuthController {
  @Public()
  @Post('login')
  login(@Body() loginDto: LoginDto) {
    // This endpoint can be accessed without authentication
  }

  @Get('profile')
  getProfile(@Request() req) {
    // This endpoint requires authentication
  }
}

How It Works

  1. The decorator uses NestJS's SetMetadata function to attach metadata to the route
  2. The JwtAuthGuard checks for this metadata using the Reflector service
  3. If the metadata is present and set to true, the guard allows the request without JWT validation

Roles Decorator

Overview

The @Roles() decorator specifies which user roles are allowed to access a particular route. It works in conjunction with the RolesGuard.

Source: src/auth/decorators/roles.decorator.ts

import { SetMetadata } from '@nestjs/common';

export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);

Usage

import { Roles } from './auth/decorators/roles.decorator';
import { RolesGuard } from './auth/guards/roles.guard';

@Controller('admin')
export class AdminController {
  @Roles('admin')
  @UseGuards(RolesGuard)
  @Get('dashboard')
  adminDashboard() {
    // Only users with the 'admin' role can access this endpoint
  }

  @Roles('admin', 'supervisor')
  @UseGuards(RolesGuard)
  @Get('reports')
  reports() {
    // Users with either 'admin' or 'supervisor' roles can access this endpoint
  }
}

How It Works

  1. The decorator accepts one or more role strings as arguments
  2. It uses SetMetadata to attach these roles to the route as metadata
  3. The RolesGuard extracts this metadata and compares it with the user's roles
  4. The guard allows the request only if the user has at least one of the required roles

Current User Decorator

Overview

The @CurrentUser() decorator provides a convenient way to access the authenticated user from the request object.

Source: src/auth/decorators/current-user.decorator.ts

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

export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

Usage

import { CurrentUser } from './auth/decorators/current-user.decorator';
import { User } from './types/interfaces/user.interface';

@Controller('user')
export class UserController {
  @Get('profile')
  getProfile(@CurrentUser() user: User) {
    // The user object is automatically extracted from the request
    return user;
  }

  @Post('update-preferences')
  updatePreferences(
    @CurrentUser() user: User,
    @Body() preferencesDto: PreferencesDto,
  ) {
    // Use the user ID from the authenticated user
    return this.userService.updatePreferences(user.id, preferencesDto);
  }
}

How It Works

  1. The decorator is created using NestJS's createParamDecorator function
  2. When the route is executed, the decorator extracts the user object from the request
  3. The user object was previously attached to the request by the JWT authentication strategy
  4. The extracted user is provided as a parameter to the route handler

Combining Decorators

These decorators can be combined to create sophisticated access control rules:

@Controller('documents')
export class DocumentController {
  @Public()
  @Get('public')
  getPublicDocuments() {
    // Accessible without authentication
  }

  @Get('user')
  getUserDocuments(@CurrentUser() user: User) {
    // Requires authentication, no specific roles
  }

  @Roles('editor', 'admin')
  @UseGuards(RolesGuard)
  @Post('create')
  createDocument(@CurrentUser() user: User, @Body() documentDto: DocumentDto) {
    // Requires authentication and specific roles
  }
}

Error Handling

When decorators are used with guards, they participate in the authentication and authorization flow:

  • Missing or invalid JWT token: 401 Unauthorized
  • Missing required roles: 403 Forbidden

Best Practices

  • Use @Public() sparingly, only for endpoints that must be accessible without authentication
  • Always combine @Roles() with @UseGuards(RolesGuard) to enforce role checks
  • Use @CurrentUser() instead of manually extracting the user from the request object