# Guards, authentication and authorisation

> Source: https://learn-nestjs.com/guards-and-auth/
> Part of Learn NestJS, free to read.

A guard runs before the route handler and returns a boolean. `false` produces a 403; throwing gives you control of the response.

```ts src/auth/jwt-auth.guard.ts
@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private readonly jwt: JwtService, private readonly reflector: Reflector) {}

  async canActivate(ctx: ExecutionContext): Promise<boolean> {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC, [
      ctx.getHandler(), ctx.getClass(),
    ]);
    if (isPublic) return true;

    const req = ctx.switchToHttp().getRequest();
    const token = req.headers.authorization?.replace(/^Bearer /, '');
    if (!token) throw new UnauthorizedException();

    try {
      req.user = await this.jwt.verifyAsync(token);
      return true;
    } catch {
      throw new UnauthorizedException();
    }
  }
}
```

## Default deny

Register the guard globally and opt routes *out*, never the reverse.

```ts src/app.module.ts
providers: [{ provide: APP_GUARD, useClass: JwtAuthGuard }]
```

```ts
export const IS_PUBLIC = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC, true);

@Public()
@Post('login')
login(@Body() dto: LoginDto) {}
```

:::warn This is the whole ballgame
An allowlist (`@UseGuards()` on the routes that need protection) fails open: the endpoint someone forgets to decorate is public. A global guard with `@Public()` opt-outs fails closed: the endpoint someone forgets is protected. Always the second one.
:::

## Roles

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

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

  canActivate(ctx: ExecutionContext): boolean {
    const required = this.reflector.getAllAndOverride<Role[]>('roles', [
      ctx.getHandler(), ctx.getClass(),
    ]);
    if (!required?.length) return true;
    const { user } = ctx.switchToHttp().getRequest();
    return required.some((r) => user?.roles?.includes(r));
  }
}
```

```ts
@Roles(Role.Admin)
@Delete(':id')
remove(@Param('id') id: string) {}
```

## The check that gets forgotten

Roles answer *what kind of user is this*. They do not answer *is this their resource*, and that second question is where the real vulnerabilities are.

```ts
@Get(':id')
findOne(@Param('id') id: string) {
  return this.invoices.findOne(id);      // any authenticated user, any invoice
}
```

Ownership depends on data, so it belongs in the service, where the query happens:

```ts
async findOne(id: string, requester: AuthUser): Promise<Invoice> {
  const invoice = await this.repo.findById(id);
  if (!invoice || invoice.ownerId !== requester.id) {
    throw new NotFoundException();       // not Forbidden: do not confirm it exists
  }
  return invoice;
}
```

Return 404 rather than 403 for resources the caller may not see. A 403 tells an attacker the id is real.

And write the test, once per resource type:

```ts
it('does not let a user read another user’s invoice', async () => {
  await request(app.getHttpServer())
    .get(`/invoices/${bobsInvoice.id}`)
    .set('Authorization', aliceToken)
    .expect(404);
});
```

## Guards, interceptors, pipes, filters

The execution order, which is worth memorising:

```text
middleware -> guards -> interceptors (before) -> pipes -> handler
           -> interceptors (after) -> exception filters
```

- **Guard** — may this proceed? Auth, roles, rate limits.
- **Pipe** — transform and validate input. DTO validation.
- **Interceptor** — wrap the call. Logging, caching, response shaping, timeouts.
- **Filter** — turn a thrown exception into a response.

Putting logic in the wrong one is a common source of surprise: a pipe cannot reject on authorisation grounds cleanly, and a guard cannot see the parsed body.

## Common questions

### Guard or middleware?

Guards know about the route — they get the handler, the class and its metadata, so they can read decorators like `@Roles()`. Middleware runs before routing and knows only the request. Use guards for anything decision-based.

### Where do I check that a user owns a record?

In the service, where the record is fetched. A guard runs before the handler and would have to query the database itself, duplicating work and drifting out of sync with the real query.

### 403 or 404 for a resource the user cannot access?

404, for anything with a guessable or enumerable id. A 403 confirms the resource exists, which is an information leak. Use 403 when the caller is allowed to know it exists but not to act.
