Dev Study
← 解説「ガード — 認証・認可の入口」に戻る

サンプルコードで身につける: ガード — 認証・認可の入口

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1必ず通す・必ず拒否する最小ガード

ガードの契約を最小単位で示す2つの実装です。canActivate() が true を返せば通過、false なら 403 で打ち切り、という2値の判定がガードのすべてであり、あらゆる認証・認可はこの上に乗ります。

import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";

@Injectable()
export class AllowAllGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    return true; // 通過 → ハンドラが実行される
  }
}

@Injectable()
export class DenyAllGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    return false; // 拒否 → 403 Forbidden で打ち切り
  }
}
// true / false を返すだけ、がガードの契約のすべて

2APIキーガード

リクエストヘッダの API キーを照合する実践的なガードです。context からリクエストを取り出してヘッダを見る、というガードの定番の体の動きを示します。社内ツールや外部連携 API で手軽に使われる認証方式です。

import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";

@Injectable()
export class ApiKeyGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    // ガードはまずリクエストを取り出すところから始まる
    const request = context.switchToHttp().getRequest();
    const apiKey = request.headers["x-api-key"];
    return apiKey === process.env.API_KEY;
  }
}
// curl -H "x-api-key: ..." を付けたリクエストだけが通過する

3canActivate の判定をプレーンTSで再現

「判定関数が false なら 403、true ならハンドラへ」というガードの動きをプレーン TypeScript で再現した例です。フレームワークがやっているのは、ハンドラの手前にこの if 文を自動で差し込むことだと分かります。

TypeScript

4適用範囲の3段階

ガードをハンドラ単位・コントローラ単位・アプリ全体のどこに掛けるかの使い分けです。実務では「全体に認証ガード + 公開エンドポイントだけ例外」の構成が多く、適用範囲の設計が認証設計そのものになります。

import { Controller, Get, UseGuards } from "@nestjs/common";
import { AuthGuard } from "./auth.guard";

@Controller("articles")
export class ArticlesController {
  @Get("drafts")
  @UseGuards(AuthGuard) // ① ハンドラ単位: 下書きはログイン必須
  findDrafts() {
    return [];
  }

  @Get() // 公開記事一覧は誰でも見られる
  findAll() {
    return [];
  }
}

// ② コントローラ単位: @Controller の上に @UseGuards(...)
// ③ アプリ全体(main.ts): app.useGlobalGuards(new AuthGuard())

5ロールによる認可ガード

「ログインしているか(認証)」の次に来る「その操作をしてよいか(認可)」を担当するガードです。認証処理が request.user を設定済みという前提でロールを調べる、管理者専用 API の実務定番パターンです。

import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";

@Injectable()
export class AdminGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    // 先に動く認証処理が request.user を設定済み、という前提
    const user = request.user;
    return user !== undefined && user.role === "admin";
  }
}

// 適用例: 管理画面系のコントローラにまとめて掛ける
// @Controller("admin")
// @UseGuards(AdminGuard)
// export class AdminController { ... }