> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication & Authorization

# Chapter 6: Authentication & Authorization

> Securing your application is critical. This chapter covers authentication (verifying identity), authorization (controlling access), JWT, OAuth, RBAC, password hashing, guards, and best practices for building secure APIs in NestJS. We'll walk through practical examples and explain the "why" behind each step.

***

## 6.1 Authentication vs Authorization

Understanding the difference between authentication and authorization is fundamental to building secure applications.

### Authentication

**Authentication answers: "Who are you?"**

* Verifies user identity
* Confirms credentials (username/password, token, etc.)
* Establishes user session
* Examples: Login, token validation

### Authorization

**Authorization answers: "What are you allowed to do?"**

* Controls access to resources
* Enforces permissions and roles
* Determines what actions are allowed
* Examples: Admin-only routes, resource ownership checks

**Analogy:**

> Think of authentication as showing your employee badge at the office entrance (proving who you are). Authorization is the keycard system on individual floors -- just because you work at the company does not mean you can enter the server room. In NestJS, JWT guards handle authentication (validating the badge), while Roles guards handle authorization (checking which rooms your badge unlocks). A common mistake is conflating the two: returning 401 (Unauthorized) when you mean 403 (Forbidden). 401 means "I do not know who you are," 403 means "I know who you are, but you are not allowed here."

### The Relationship

```text theme={null}
Authentication (Who) → Authorization (What)
    ↓                      ↓
  Login              →  Access Control
  Token Validation   →  Role Checks
  Identity Verify    →  Permission Checks
```

### Authentication Strategy Comparison

| Strategy                | Stateless? | Best For                        | Token Storage                                | Revocation                    | Scalability                            |
| ----------------------- | ---------- | ------------------------------- | -------------------------------------------- | ----------------------------- | -------------------------------------- |
| **JWT (Access Token)**  | Yes        | APIs, SPAs, mobile              | Client (memory/cookie)                       | Hard (need blocklist)         | Excellent (no server state)            |
| **JWT + Refresh Token** | Hybrid     | Production APIs                 | Access in memory, refresh in httpOnly cookie | Moderate (revoke refresh)     | Excellent                              |
| **Session (Cookie)**    | No         | Server-rendered apps            | Server (Redis/DB)                            | Easy (delete session)         | Moderate (session store is bottleneck) |
| **OAuth2 (Social)**     | Depends    | "Login with Google/GitHub"      | Delegated to provider                        | Moderate (revoke at provider) | Excellent                              |
| **API Key**             | Yes        | Service-to-service, public APIs | Client config                                | Easy (delete key from DB)     | Excellent                              |
| **mTLS**                | Yes        | Service mesh, zero-trust        | Certificate                                  | Moderate (CRL/OCSP)           | Excellent                              |

**Decision Framework -- Which Auth Strategy?**

```text theme={null}
Is the client a browser SPA?
  --> JWT + Refresh Token (access token in memory, refresh in httpOnly cookie)
  --> Consider session-based if you need easy revocation (user clicks "log out everywhere")

Is the client a mobile app?
  --> JWT + Refresh Token (store refresh token in secure storage)

Is it server-to-server communication?
  --> API keys (simple) or mTLS (high security)

Do you need "Login with Google/GitHub"?
  --> OAuth2 via Passport strategies (can combine with JWT for your own tokens)

Do you need instant token revocation (e.g., "ban this user immediately")?
  --> Session-based auth OR JWT with a Redis blocklist
```

### RBAC vs ABAC vs PBAC

| Model                      | How Access Is Decided                                      | Complexity | Best For                                              |
| -------------------------- | ---------------------------------------------------------- | ---------- | ----------------------------------------------------- |
| **RBAC** (Role-Based)      | User's role matches required role                          | Low        | Most applications ("admin can delete")                |
| **ABAC** (Attribute-Based) | User/resource/environment attributes evaluated             | High       | Complex policies ("EU users can only access EU data") |
| **PBAC** (Policy-Based)    | Policies evaluated at runtime (like ABAC but externalized) | Very High  | Enterprises with dynamic compliance rules             |

For most NestJS applications, RBAC is sufficient. Start with roles, add permissions if you need finer granularity, and only move to ABAC when your access rules depend on resource attributes (like "owner can edit their own posts but not others").

***

## 6.2 Password Hashing

Before implementing authentication, you must understand password security. Never store passwords in plain text.

### Why Hash Passwords?

* **Security**: Even if database is compromised, passwords are protected
* **Privacy**: Developers can't see user passwords
* **Compliance**: Required by security standards (GDPR, PCI-DSS)

### Using bcrypt

bcrypt is the industry standard for password hashing. It is intentionally slow -- that is a feature, not a bug. Each hash takes \~100ms, which makes brute-force attacks impractical (an attacker trying 1 billion passwords would need \~3 years). The "salt rounds" parameter controls this slowness: each increment roughly doubles the time.

```bash theme={null}
npm install bcrypt
npm install --save-dev @types/bcrypt
```

```typescript theme={null}
// hash/hash.service.ts
import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcrypt';

@Injectable()
export class HashService {
  async hash(password: string): Promise<string> {
    // Salt rounds = 10 means 2^10 (1024) iterations of the hashing algorithm.
    // This takes ~100ms on modern hardware -- slow enough to deter brute force,
    // fast enough for a normal login flow. Do not go below 10 in production.
    // If you need more security, use 12 (at the cost of ~400ms per hash).
    const saltRounds = 10;
    // bcrypt.hash() automatically generates a random salt and embeds it in
    // the output. You do not need to store the salt separately.
    return bcrypt.hash(password, saltRounds);
  }

  async compare(password: string, hash: string): Promise<boolean> {
    // bcrypt.compare() extracts the salt from the stored hash and uses it
    // to hash the incoming password, then compares the results.
    // It is timing-safe, so it does not leak information via response time.
    return bcrypt.compare(password, hash);
  }
}
```

**Common Mistake:** Using `bcryptjs` (pure JavaScript) instead of `bcrypt` (native C++ bindings). The pure JS version is 3-5x slower and can block the Node.js event loop under high load. Use the native `bcrypt` package unless you cannot compile native modules in your environment.

### Password Validation

```typescript theme={null}
// dto/register.dto.ts
import { IsString, MinLength, Matches } from 'class-validator';

export class RegisterDto {
  @IsString()
  @MinLength(8)
  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/, {
    message: 'Password must contain uppercase, lowercase, number, and special character',
  })
  password: string;
}
```

***

## 6.3 JWT Authentication

JSON Web Tokens (JWT) are a popular way to implement stateless authentication in APIs. JWTs are signed tokens that clients send with each request to prove their identity.

### How JWT Works

1. User logs in with credentials
2. Server validates credentials
3. Server creates and signs a JWT
4. Client stores the JWT
5. Client sends JWT in `Authorization` header
6. Server verifies JWT and extracts user info

**Diagram: JWT Flow**

```text theme={null}
Client → [Login Request] → Server
    ↓
Server validates credentials
    ↓
Server creates JWT (signed)
    ↓
Server returns JWT → Client
    ↓
Client stores JWT
    ↓
Client → [Request with JWT] → Server
    ↓
Server verifies JWT
    ↓
Server grants access
```

### Installing JWT Package

```bash theme={null}
npm install @nestjs/jwt @nestjs/passport passport passport-jwt
npm install --save-dev @types/passport-jwt
```

### JWT Module Setup

```typescript theme={null}
// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './strategies/jwt.strategy';
import { LocalStrategy } from './strategies/local.strategy';

@Module({
  imports: [
    PassportModule,
    // JwtModule.register() is a dynamic module that configures the JWT service.
    // In production, NEVER use a hardcoded fallback secret -- if JWT_SECRET is
    // missing, the app should crash at startup, not silently use an insecure default.
    JwtModule.register({
      secret: process.env.JWT_SECRET || 'your-secret-key', // Use registerAsync() with ConfigService in production
      signOptions: { expiresIn: '1d' },  // Tokens expire after 1 day
    }),
  ],
  controllers: [AuthController],
  // LocalStrategy and JwtStrategy are providers because Passport needs them
  // registered in the DI container to resolve them when guards activate.
  providers: [AuthService, JwtStrategy, LocalStrategy],
  exports: [AuthService],  // Export so other modules can verify tokens
})
export class AuthModule {}
```

**Production Pattern:** Use `JwtModule.registerAsync()` with ConfigService to load the secret from environment variables safely, and validate that it is set before the app starts:

```typescript theme={null}
JwtModule.registerAsync({
  imports: [ConfigModule],
  useFactory: (config: ConfigService) => ({
    secret: config.getOrThrow('JWT_SECRET'),  // Crashes at startup if missing
    signOptions: { expiresIn: '15m' },         // Short-lived access tokens
  }),
  inject: [ConfigService],
}),
```

### Auth Service

```typescript theme={null}
// auth/auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import { HashService } from '../hash/hash.service';
import { RegisterDto } from './dto/register.dto';
import { LoginDto } from './dto/login.dto';

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService,
    private hashService: HashService,
  ) {}

  async register(registerDto: RegisterDto) {
    const hashedPassword = await this.hashService.hash(registerDto.password);

    const user = await this.usersService.create({
      ...registerDto,
      password: hashedPassword,
    });

    const { password, ...result } = user;
    return result;
  }

  async validateUser(email: string, password: string): Promise<any> {
    const user = await this.usersService.findByEmail(email);
    
    if (!user) {
      throw new UnauthorizedException('Invalid credentials');
    }

    const isPasswordValid = await this.hashService.compare(password, user.password);
    
    if (!isPasswordValid) {
      throw new UnauthorizedException('Invalid credentials');
    }

    const { password: _, ...result } = user;
    return result;
  }

  async login(loginDto: LoginDto) {
    const user = await this.validateUser(loginDto.email, loginDto.password);
    
    const payload = { email: user.email, sub: user.id, roles: user.roles };
    
    return {
      access_token: this.jwtService.sign(payload),
      user: {
        id: user.id,
        email: user.email,
        name: user.name,
      },
    };
  }

  async validateToken(token: string) {
    try {
      return this.jwtService.verify(token);
    } catch (error) {
      throw new UnauthorizedException('Invalid token');
    }
  }
}
```

### JWT Strategy

```typescript theme={null}
// auth/strategies/jwt.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UsersService } from '../../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private usersService: UsersService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: process.env.JWT_SECRET || 'your-secret-key',
    });
  }

  async validate(payload: any) {
    const user = await this.usersService.findOne(payload.sub);
    
    if (!user) {
      throw new UnauthorizedException();
    }

    return { id: user.id, email: user.email, roles: user.roles };
  }
}
```

### Local Strategy (Username/Password)

```typescript theme={null}
// auth/strategies/local.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-local';
import { AuthService } from '../auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({ usernameField: 'email' });
  }

  async validate(email: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(email, password);
    
    if (!user) {
      throw new UnauthorizedException();
    }
    
    return user;
  }
}
```

### Auth Controller

```typescript theme={null}
// auth/auth.controller.ts
import { Controller, Post, Body, UseGuards, Get, Request } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalAuthGuard } from './guards/local-auth.guard';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { RegisterDto } from './dto/register.dto';
import { LoginDto } from './dto/login.dto';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @Post('register')
  async register(@Body() registerDto: RegisterDto) {
    return this.authService.register(registerDto);
  }

  @UseGuards(LocalAuthGuard)
  @Post('login')
  async login(@Body() loginDto: LoginDto, @Request() req) {
    return this.authService.login(req.user);
  }

  @UseGuards(JwtAuthGuard)
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
  }
}
```

### JWT Auth Guard

```typescript theme={null}
// auth/guards/jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
```

### Local Auth Guard

```typescript theme={null}
// auth/guards/local-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
```

### Using JWT Guard

```typescript theme={null}
// users/users.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';

@Controller('users')
@UseGuards(JwtAuthGuard)  // Protect all routes
export class UsersController {
  @Get()
  findAll() {
    return [];
  }
}
```

***

## 6.4 Authorization: Roles & Permissions

Role-Based Access Control (RBAC) restricts access based on user roles. This lets you control who can do what in your app.

### Roles Decorator

```typescript theme={null}
// auth/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';

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

### Roles Guard

```typescript theme={null}
// auth/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Roles } from '../decorators/roles.decorator';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
    
    if (!requiredRoles) {
      return true;  // No roles required
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;

    if (!user || !user.roles) {
      return false;
    }

    return requiredRoles.some(role => user.roles.includes(role));
  }
}
```

### Using Roles

```typescript theme={null}
// users/users.controller.ts
import { Controller, Delete, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';

@Controller('users')
@UseGuards(JwtAuthGuard, RolesGuard)
export class UsersController {
  @Delete(':id')
  @Roles('admin')
  remove(@Param('id') id: string) {
    return this.usersService.remove(id);
  }
}
```

### Permission-Based Authorization

For more granular control, use permissions:

```typescript theme={null}
// auth/decorators/permissions.decorator.ts
import { SetMetadata } from '@nestjs/common';

export const Permissions = (...permissions: string[]) => 
  SetMetadata('permissions', permissions);

// auth/guards/permissions.guard.ts
@Injectable()
export class PermissionsGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredPermissions = this.reflector.get<string[]>(
      'permissions',
      context.getHandler(),
    );

    if (!requiredPermissions) {
      return true;
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;

    return requiredPermissions.every(permission => 
      user.permissions?.includes(permission)
    );
  }
}
```

***

## 6.5 OAuth2 & Social Login

NestJS supports OAuth2 via Passport strategies. This lets users log in with Google, Facebook, GitHub, etc.

### Installing Passport OAuth

```bash theme={null}
npm install @nestjs/passport passport-google-oauth20
npm install --save-dev @types/passport-google-oauth20
```

### Google Strategy

```typescript theme={null}
// auth/strategies/google.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor() {
    super({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: process.env.GOOGLE_CALLBACK_URL,
      scope: ['email', 'profile'],
    });
  }

  async validate(
    accessToken: string,
    refreshToken: string,
    profile: any,
    done: VerifyCallback,
  ): Promise<any> {
    const { name, emails, photos } = profile;
    const user = {
      email: emails[0].value,
      firstName: name.givenName,
      lastName: name.familyName,
      picture: photos[0].value,
      accessToken,
    };
    done(null, user);
  }
}
```

### Google Auth Controller

```typescript theme={null}
// auth/auth.controller.ts
import { Controller, Get, UseGuards, Req } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @Get('google')
  @UseGuards(AuthGuard('google'))
  async googleAuth(@Req() req) {
    // Initiates Google OAuth flow
  }

  @Get('google/callback')
  @UseGuards(AuthGuard('google'))
  async googleAuthRedirect(@Req() req) {
    const user = req.user;
    
    // Create or find user in database
    const dbUser = await this.authService.findOrCreateUser(user);
    
    // Generate JWT
    const token = this.authService.generateToken(dbUser);
    
    return { access_token: token, user: dbUser };
  }
}
```

***

## 6.6 Refresh Tokens

Refresh tokens allow users to get new access tokens without re-authenticating.

### Implementing Refresh Tokens

```typescript theme={null}
// auth/auth.service.ts
async login(loginDto: LoginDto) {
  const user = await this.validateUser(loginDto.email, loginDto.password);
  
  const payload = { email: user.email, sub: user.id };
  
  const accessToken = this.jwtService.sign(payload, { expiresIn: '15m' });
  const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' });

  // Store refresh token in database
  await this.usersService.updateRefreshToken(user.id, refreshToken);

  return {
    access_token: accessToken,
    refresh_token: refreshToken,
    user: {
      id: user.id,
      email: user.email,
    },
  };
}

async refreshToken(refreshToken: string) {
  try {
    const payload = this.jwtService.verify(refreshToken);
    const user = await this.usersService.findOne(payload.sub);

    if (user.refreshToken !== refreshToken) {
      throw new UnauthorizedException('Invalid refresh token');
    }

    const newPayload = { email: user.email, sub: user.id };
    return {
      access_token: this.jwtService.sign(newPayload, { expiresIn: '15m' }),
    };
  } catch (error) {
    throw new UnauthorizedException('Invalid refresh token');
  }
}
```

***

## 6.7 Multi-Factor Authentication (MFA)

Enhance security by requiring a second factor (e.g., OTP, email code) after password login.

### MFA Service

```typescript theme={null}
// auth/mfa.service.ts
import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { EmailService } from '../email/email.service';

@Injectable()
export class MfaService {
  constructor(
    private usersService: UsersService,
    private emailService: EmailService,
  ) {}

  async generateOTP(userId: number): Promise<string> {
    const otp = Math.floor(100000 + Math.random() * 900000).toString();
    const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes

    await this.usersService.saveOTP(userId, otp, expiresAt);
    await this.emailService.sendOTP(userId, otp);

    return otp;
  }

  async verifyOTP(userId: number, otp: string): Promise<boolean> {
    const user = await this.usersService.findOne(userId);
    
    if (!user.otp || user.otp !== otp) {
      return false;
    }

    if (new Date() > user.otpExpiresAt) {
      return false;
    }

    await this.usersService.clearOTP(userId);
    return true;
  }
}
```

***

## 6.8 Auth Edge Cases

**Edge Case 1: JWT token rotation and race conditions**

When a client's access token expires and it sends a refresh request, what happens if two simultaneous requests both try to refresh at the same time? Both carry the same refresh token, but after the first request succeeds and rotates the token, the second request has a stale refresh token. Without handling this, the second request fails and the user gets logged out. Solutions: (1) Allow a short grace period where the old refresh token is still valid; (2) Use a token family approach where any reuse of an old token invalidates the entire family (detecting token theft).

**Edge Case 2: The "log out everywhere" problem with JWT**

JWTs are stateless -- once issued, they are valid until expiration. If a user changes their password and wants to invalidate all existing sessions, you cannot "revoke" a JWT. Solutions: (1) Keep a Redis blocklist of revoked JWTs (checked on every request via a guard); (2) Store a `tokenVersion` on the user record and increment it on password change -- the JWT strategy's `validate()` method checks if the token's version matches the database; (3) Use short-lived access tokens (5-15 minutes) so revocation is eventually consistent.

**Edge Case 3: OAuth provider returns inconsistent email**

Some OAuth providers (notably Apple Sign In) can return a "private relay" email that is different every time the user signs in from a different app. You cannot rely solely on email for user matching. Store the provider's unique user ID (`sub` claim) alongside the email, and match on the provider ID first.

**Edge Case 4: Guard execution with GraphQL**

GraphQL resolvers do not have the same `ExecutionContext` as REST controllers. When using `@UseGuards(JwtAuthGuard)` on a resolver, the guard receives a GraphQL context, not an HTTP request. The `JwtStrategy` expects `ExtractJwt.fromAuthHeaderAsBearerToken()`, which reads from the HTTP request. You need to configure the GraphQL module to pass the HTTP request through: `GraphQLModule.forRoot({ context: ({ req }) => ({ req }) })`.

**Edge Case 5: Rate limiting with distributed instances**

The `@nestjs/throttler` module stores rate limit counts in memory by default. If you run 3 instances behind a load balancer, each instance has its own counter -- an attacker can make 30 requests (10 per instance) before any instance blocks them. Use the `ThrottlerStorageRedisService` to share counts across instances.

***

## 6.8 Security Best Practices

Following security best practices protects your application and users.

### Password Security

* Always hash passwords (use bcrypt with salt rounds >= 10)
* Enforce strong password policies
* Never log or return passwords
* Use password reset tokens (time-limited)

### JWT Security

* Use strong, random secrets
* Set appropriate expiration times
* Use HTTPS in production
* Store secrets in environment variables
* Consider refresh tokens for long sessions

### HTTP Security Headers

```typescript theme={null}
// main.ts
import helmet from 'helmet';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(helmet());
  await app.listen(3000);
}
```

### Rate Limiting

Rate limiting is your first defense against brute-force login attacks and API abuse. Without it, an attacker can try thousands of passwords per second against your login endpoint.

```bash theme={null}
npm install @nestjs/throttler
```

```typescript theme={null}
// app.module.ts
import { ThrottlerModule } from '@nestjs/throttler';

@Module({
  imports: [
    // Global rate limit: 10 requests per 60 seconds per IP.
    // For login endpoints, apply a stricter limit (e.g., 5 per minute).
    ThrottlerModule.forRoot({
      ttl: 60,    // Time window in seconds
      limit: 10,  // Maximum requests per window
    }),
  ],
})
export class AppModule {}
```

**Practical Tip:** Apply different rate limits to different endpoints. Public endpoints can be generous (100/min), but authentication endpoints should be strict (5/min). Use `@Throttle()` decorator on individual routes to override the global limit:

```typescript theme={null}
@Throttle({ default: { limit: 5, ttl: 60 } })
@Post('login')
async login(@Body() loginDto: LoginDto) { ... }
```

### CORS Configuration

```typescript theme={null}
// main.ts
app.enableCors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
  credentials: true,
});
```

### Input Validation

Always validate and sanitize input:

```typescript theme={null}
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  }),
);
```

### Security Checklist

* Always hash passwords
* Use HTTPS in production
* Store secrets in environment variables
* Set secure HTTP headers (helmet)
* Limit login attempts (prevent brute force)
* Validate and sanitize all input
* Keep dependencies up to date
* Use CORS to restrict allowed origins
* Log authentication events for auditing
* Implement rate limiting
* Use refresh tokens for long sessions
* Consider MFA for sensitive applications

***

## 6.9 Real-World Example: Complete Auth Module

Here's a complete authentication module structure:

```
auth/
├── auth.module.ts
├── auth.service.ts
├── auth.controller.ts
├── strategies/
│   ├── jwt.strategy.ts
│   ├── local.strategy.ts
│   └── google.strategy.ts
├── guards/
│   ├── jwt-auth.guard.ts
│   ├── local-auth.guard.ts
│   └── roles.guard.ts
├── decorators/
│   ├── roles.decorator.ts
│   └── permissions.decorator.ts
└── dto/
    ├── login.dto.ts
    ├── register.dto.ts
    └── refresh-token.dto.ts
```

***

## 6.10 Summary

You've learned how to secure your NestJS APIs:

**Key Concepts:**

* **Authentication**: Verifying user identity
* **Authorization**: Controlling access to resources
* **JWT**: Stateless token-based authentication
* **OAuth2**: Social login integration
* **RBAC**: Role-based access control
* **Password Hashing**: Secure password storage
* **MFA**: Multi-factor authentication

**Best Practices:**

* Always hash passwords
* Use HTTPS in production
* Store secrets in environment variables
* Set secure HTTP headers
* Implement rate limiting
* Validate all input
* Use refresh tokens
* Log authentication events

**Next Chapter:** Learn about testing strategies, including unit tests, integration tests, and E2E tests.
