ガードの使用方法と認証の実装方法

NestJSのガード機能を理解し、認証の実装を行いましょう。

概要

NestJSには、リクエストの処理を続行するかどうかを決定するためのガードという機能が存在します。ガードはCanActivateインターフェースを実装することで利用可能で、特に認証の処理によく用いられます。

ガードは主に以下の2つの役割を果たします。

  1. 認証: ユーザーの権限やロール、ACLの検証を行い、リクエストの処理を続行するかどうかを判定します。
  2. リクエストの制御: ミドルウェアとインターセプターの間で動作し、リクエストの処理を制御します。

この記事では、NestJSのガードの使用方法と、ガードを用いた認証の実装方法について解説します。

ガードの作成と利用

NestJSのCLIツールを使用してガードを作成することができます。以下のコマンドを実行すると、AuthGuardという名前のガードが作成されます。

nest g guard users/guard/auth

作成されたガードは、CanActivateインターフェースを実装しており、canActivateメソッドを持っています。このメソッドは、リクエストの処理を続行するかどうかを決定するためのものです。

以下に、クエリパラメータが存在しない場合にリクエストを拒否するガードの例を示します。

/src/users/guard/auth/auth.guard.ts

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

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return !!request.query.params;
  }
}

上記のコードでは、canActivateメソッドがリクエストのクエリパラメータをチェックし、paramsパラメータが存在する場合にのみリクエストの処理を続行します。

作成したガードを使用するには、@UseGuardsデコレータを使用してコントローラーまたはルートハンドラにバインドします。以下に例を示します。

import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Body,
  Param,
  HttpException,
  HttpStatus,
  UseFilters,
  ParseIntPipe,
  UseGuards,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { ForbiddenException } from './exceptions/forbidden.exception';
import { HttpExceptionFilter } from './filters/http-exception/http-exception.filter';
import { AuthGuard } from './guard/auth/auth.guard';

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

  @Get()
  getUsers(): User[] {
    return this.usersService.getUsers();
  }
  @Post()
  addUser(@Body() name: string): void {
    this.usersService.addUser(name);
  }
  @Put(':id')
  putUser(
    @Param('id', ParseIntPipe) id: number,
    @Body('name') name: string,
  ): void {
    this.usersService.putUser(id, name);
  }
  @Delete(':id')
  deleteUser(@Param('id', ParseIntPipe) id: number): void {
    this.usersService.deleteUser(id);
  }
  @Get('throw')
  getException(): string {
    throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
  }
  @Get('custom_throw')
  getCustomException(): string {
    throw new ForbiddenException();
  }
  @Get('guard')
  @UseGuards(AuthGuard)
  getGuard(): string {
    return 'Guard is working';
  }
}

上記のコードでは、@UseGuardsデコレータを使用してAuthGuardgetHelloルートハンドラにバインドしています。これにより、getHelloへのリクエストがあったときには、まずAuthGuardcanActivateメソッドが実行され、その結果に基づいてリクエストの処理が続行されます。

テスト

## パラメーターがない場合通りません。
curl http://localhost:3000/users/guard
## パラメーターがある場合通ります。
curl http://localhost:3000/users/guard?params=true

まとめ

NestJSのガードは、リクエストの処理を続行するかどうかを決定する強力な機能です。特に、ユーザーの認証や権限の検証を行う際には、ガードを活用することで効率的に処理を行うことができます。ガードの使用方法を理解し、適切に活用していきましょう。