HIGH auth bypassnestjstypescript

Auth Bypass in Nestjs (Typescript)

Typescript-Specific Remediation in Nestjs — concrete code fixes

To mitigate auth bypass vulnerabilities in NestJS with TypeScript, enforce authentication at the most granular level possible and leverage TypeScript’s type system to prevent unsafe assumptions. Always apply guards at the route level for sensitive endpoints, even if controller-level guards exist, and use custom decorators that combine metadata with runtime validation.

Replace ad-hoc metadata checks with a robust AuthGuard that explicitly validates the presence and integrity of the user object. For example, instead of relying on request.user being set by a previous guard, verify it within the guard using a service that checks token validity and user existence:

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise {
    const request = context.switchToHttp().getRequest();
    const authHeader = request.headers.authorization;

    if (!authHeader?.startsWith('Bearer ')) {
      throw new UnauthorizedException('Missing or malformed token');
    }

    const token = authHeader.split(' ')[1];
    try {
      const payload = this.jwtService.verify(token, { secret: process.env.JWT_SECRET });
      // Attach validated user to request — TypeScript knows this is safe after verification
      request.user = { userId: payload.sub, email: payload.email, roles: payload.roles };
      return true;
    } catch (error) {
      throw new UnauthorizedException('Invalid or expired token');
    }
  }
}

Use this guard explicitly on routes that require authentication, avoiding blanket controller-level application when some routes should be public:

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

  @Get()
  @UseGuards(JwtAuthGuard) // All routes under this controller require auth by default
  findAll() {
    return this.usersService.findAll();
  }

  @Get('public')
  // Explicitly allow public access — overrides controller guard
  @UseGuards()
  getPublicInfo() {
    return { message: 'Publicly accessible' };
  }

  @Get('profile')
  @UseGuards(JwtAuthGuard) // Re-apply guard for clarity and safety
  getProfile(@Req() req) {
    // TypeScript knows req.user is defined and has expected structure
    return { userId: req.user.userId, email: req.user.email };
  }
}

Additionally, use TypeScript interfaces to strictly type the request.user property across the application. Declare an augmentation in src/types/express.index.d.ts:

// src/types/express.index.d.ts
import { User } from '../users/user.entity';

declare namespace Express {
  interface Request {
    user: User | null;
  }
}

This ensures that any attempt to access request.user without proper guard validation results in a compile-time error if the property is missing or incorrectly typed, closing a common bypass vector where guards are skipped but code assumes authentication.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

Can TypeScript interfaces prevent auth bypass in NestJS?
TypeScript interfaces alone cannot prevent runtime auth bypass, but they can eliminate entire classes of bugs by ensuring that code accessing request.user only compiles when a guard has properly set and typed that property. Combined with explicit guard usage, this creates a compile-time safety net for authentication logic.
Why is it dangerous to rely only on controller-level guards in NestJS?
Controller-level guards can be bypassed by child routes that either override them with an empty @UseGuards() array or disable them via custom metadata like setMetadata('skip-auth', true). This creates a false sense of security—TypeScript will not warn you if a route unintentionally excludes the guard, leading to potential auth bypass.