NestJSでエンタープライズレベルの認証システムを構築する際、Passportは最も信頼性の高い選択肢です。Node.jsエコシステムで長年使われてきたPassportライブラリを、NestJSの依存性注入システムと統合することで、柔軟で拡張性のある認証基盤を実現できます。本記事では、@nestjs/passportパッケージを使用したLocalStrategy(ユーザー名/パスワード認証)とJwtStrategy(トークン検証)の実装方法を解説します。

実行環境と前提条件

本記事の内容を実践するにあたり、以下の環境を前提としています。

項目 バージョン・要件
Node.js 20以上
npm 10以上
NestJS 11.x
@nestjs/passport 11.x
@nestjs/jwt 11.x
passport 0.7.x
passport-local 1.x
passport-jwt 4.x
TypeScript 5.x
OS Windows / macOS / Linux
エディタ VS Code(推奨)

事前に以下の準備を完了してください。

  • NestJS CLIのインストール済み
  • NestJSプロジェクトの作成済み
  • Module、Controller、Service、Guardの基本理解
  • JWTの仕組みについての基礎知識

NestJSプロジェクトの作成方法はNestJS入門記事を参照してください。

Passportとは何か

Passportは、Node.jsで最も広く使われている認証ライブラリです。500以上の認証戦略(Strategy)をサポートしており、ユーザー名/パスワード認証からOAuth、OpenID Connectまで、あらゆる認証方式に対応できます。

flowchart TB
    subgraph Passport["Passportフレームワーク"]
        Core[Passport Core]
        Core --> S1[LocalStrategy]
        Core --> S2[JwtStrategy]
        Core --> S3[OAuth2Strategy]
        Core --> S4[GoogleStrategy]
        Core --> S5[その他500以上]
    end
    
    subgraph NestJS["NestJS統合"]
        NP["@nestjs/passport"]
        NP --> PS[PassportStrategy]
        PS --> AG[AuthGuard]
    end
    
    S1 & S2 --> NP
    
    style Core fill:#4CAF50,color:#fff
    style NP fill:#E91E63,color:#fff

@nestjs/passportの役割

@nestjs/passportは、Passportの認証戦略をNestJSのアーキテクチャにシームレスに統合するためのラッパーモジュールです。

機能 説明
PassportStrategy 認証戦略をNestJSのProviderとして実装するための基底クラス
AuthGuard 認証戦略を適用するためのGuard
PassportModule Passportの設定を管理するモジュール
validate()メソッド 認証ロジックを実装するためのフック

直接的なJWT検証との違い

前回の記事で実装したJWT検証アプローチと、Passportを使用したアプローチの違いを理解しましょう。

観点 直接的なJWT検証 Passportを使用
実装方法 JwtServiceで直接検証 JwtStrategyのvalidate()で検証
拡張性 新しい認証方式の追加に手間がかかる 新しいStrategyを追加するだけ
コード量 シンプルな場合は少ない やや多いが構造化されている
複数認証 条件分岐が複雑になる 複数Strategyを自然に組み合わせ可能
エコシステム 自前実装が必要 500以上のStrategy利用可能

小規模なアプリケーションでは直接的なJWT検証で十分ですが、複数の認証方式を組み合わせる場合やOAuth連携を見据える場合は、Passportの採用が推奨されます。

パッケージのインストール

Passport認証に必要なパッケージをインストールします。

1
2
3
4
5
6
7
8
# Passport関連パッケージ
npm install --save @nestjs/passport passport passport-local passport-jwt

# JWT関連パッケージ(未インストールの場合)
npm install --save @nestjs/jwt

# 型定義ファイル
npm install --save-dev @types/passport-local @types/passport-jwt

インストール後、package.jsonの依存関係を確認してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "dependencies": {
    "@nestjs/jwt": "^11.0.2",
    "@nestjs/passport": "^11.0.5",
    "passport": "^0.7.0",
    "passport-jwt": "^4.0.1",
    "passport-local": "^1.0.0"
  },
  "devDependencies": {
    "@types/passport-jwt": "^4.0.2",
    "@types/passport-local": "^1.0.38"
  }
}

認証モジュールの構成

Passport認証を実装するためのモジュール構成を設計します。

flowchart TB
    subgraph AuthModule["AuthModule"]
        AS[AuthService]
        LS[LocalStrategy]
        JS[JwtStrategy]
        AC[AuthController]
    end
    
    subgraph UsersModule["UsersModule"]
        US[UsersService]
    end
    
    subgraph External["外部モジュール"]
        PM[PassportModule]
        JM[JwtModule]
    end
    
    AuthModule --> UsersModule
    AuthModule --> PM
    AuthModule --> JM
    LS --> AS
    JS --> AS
    AS --> US
    
    style LS fill:#FF5722,color:#fff
    style JS fill:#4CAF50,color:#fff

ファイル構成

src/
├── auth/
│   ├── auth.module.ts
│   ├── auth.service.ts
│   ├── auth.controller.ts
│   ├── strategies/
│   │   ├── local.strategy.ts
│   │   └── jwt.strategy.ts
│   ├── guards/
│   │   ├── local-auth.guard.ts
│   │   └── jwt-auth.guard.ts
│   ├── dto/
│   │   └── login.dto.ts
│   └── constants.ts
└── users/
    ├── users.module.ts
    └── users.service.ts

UsersModuleの実装

認証に必要なユーザー情報を管理するモジュールを実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';

export interface User {
  userId: number;
  username: string;
  password: string;
  roles: string[];
}

@Injectable()
export class UsersService {
  // 本番環境ではデータベースから取得
  private readonly users: User[] = [
    {
      userId: 1,
      username: 'admin',
      password: '$2b$10$abcdefghijklmnopqrstuv', // bcryptハッシュ化済み
      roles: ['admin', 'user'],
    },
    {
      userId: 2,
      username: 'user',
      password: '$2b$10$wxyzabcdefghijklmnopqr', // bcryptハッシュ化済み
      roles: ['user'],
    },
  ];

  async findOne(username: string): Promise<User | undefined> {
    return this.users.find((user) => user.username === username);
  }

  async findById(userId: number): Promise<User | undefined> {
    return this.users.find((user) => user.userId === userId);
  }
}
1
2
3
4
5
6
7
8
9
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';

@Module({
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

本番環境では、TypeORMやPrismaなどのORMを使用してデータベースからユーザー情報を取得します。パスワードは必ずbcryptなどでハッシュ化して保存してください。

LocalStrategyの実装

LocalStrategyは、ユーザー名とパスワードによる認証を処理します。ログインエンドポイントで使用される戦略です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// src/auth/strategies/local.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from '../auth.service';

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

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException('ユーザー名またはパスワードが正しくありません');
    }
    return user;
  }
}

PassportStrategyの仕組み

PassportStrategyは、Passportの戦略クラスをNestJSのProviderとして利用するためのミックスイン関数です。

sequenceDiagram
    participant C as クライアント
    participant G as LocalAuthGuard
    participant P as Passport
    participant LS as LocalStrategy
    participant AS as AuthService

    C->>G: POST /auth/login (username, password)
    G->>P: authenticate('local')
    P->>LS: validate(username, password)
    LS->>AS: validateUser(username, password)
    AS-->>LS: ユーザー情報 or null
    alt ユーザー有効
        LS-->>P: ユーザー情報
        P-->>G: request.user = ユーザー情報
        G-->>C: 認証成功
    else ユーザー無効
        LS-->>P: UnauthorizedException
        P-->>G: 認証失敗
        G-->>C: 401 Unauthorized
    end

LocalStrategyのオプション

デフォルトでは、リクエストボディのusernamepasswordフィールドが使用されます。別のフィールド名を使用する場合は、super()にオプションを渡します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// メールアドレスでログインする場合
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({
      usernameField: 'email',
      passwordField: 'password',
    });
  }

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

JwtStrategyの実装

JwtStrategyは、リクエストに含まれるJWTトークンを検証します。保護されたエンドポイントへのアクセス時に使用されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// src/auth/strategies/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { jwtConstants } from '../constants';
import { UsersService } from '../../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private usersService: UsersService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: jwtConstants.secret,
    });
  }

  async validate(payload: any) {
    // payloadはJWTのデコード結果
    // トークン無効化チェックやユーザー存在確認をここで行う
    const user = await this.usersService.findById(payload.sub);
    if (!user) {
      throw new UnauthorizedException('ユーザーが見つかりません');
    }
    return { userId: payload.sub, username: payload.username, roles: user.roles };
  }
}

JwtStrategyのオプション詳細

オプション 説明
jwtFromRequest JWTをどこから抽出するか指定
ignoreExpiration 有効期限切れトークンを許可するか(通常はfalse)
secretOrKey トークン署名の検証に使用する秘密鍵
algorithms 使用するアルゴリズム(デフォルト: HS256)
issuer トークンの発行者を検証
audience トークンの対象者を検証

JWTの抽出方法

ExtractJwtには複数の抽出メソッドが用意されています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { ExtractJwt } from 'passport-jwt';

// Authorizationヘッダーからベアラートークンとして抽出(推奨)
ExtractJwt.fromAuthHeaderAsBearerToken()

// クエリパラメータから抽出
ExtractJwt.fromUrlQueryParameter('token')

// リクエストボディから抽出
ExtractJwt.fromBodyField('access_token')

// カスタムヘッダーから抽出
ExtractJwt.fromHeader('x-access-token')

// 複数の方法を組み合わせ
ExtractJwt.fromExtractors([
  ExtractJwt.fromAuthHeaderAsBearerToken(),
  ExtractJwt.fromUrlQueryParameter('token'),
])

AuthServiceの実装

ユーザー検証とJWTトークン生成を担当するサービスを実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';

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

  async validateUser(username: string, password: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && (await bcrypt.compare(password, user.password))) {
      // パスワードを除外したユーザー情報を返す
      const { password: _, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: any) {
    // JWTのペイロードを作成
    const payload = {
      username: user.username,
      sub: user.userId,
      roles: user.roles,
    };

    return {
      access_token: this.jwtService.sign(payload),
      token_type: 'Bearer',
      expires_in: 3600, // 1時間
    };
  }
}

認証ガードの実装

LocalStrategyとJwtStrategyに対応するGuardを作成します。

1
2
3
4
5
6
// src/auth/guards/local-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
1
2
3
4
5
6
// src/auth/guards/jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

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

AuthGuardの動作原理

AuthGuardは、指定された戦略名に基づいてPassportの認証プロセスを実行します。

sequenceDiagram
    participant R as リクエスト
    participant AG as AuthGuard('jwt')
    participant P as Passport
    participant JS as JwtStrategy
    participant C as Controller

    R->>AG: canActivate()
    AG->>P: authenticate('jwt')
    P->>JS: トークン抽出 & 検証
    JS->>JS: validate(payload)
    alt 認証成功
        JS-->>P: ユーザー情報
        P-->>AG: request.user設定
        AG-->>R: true
        R->>C: ハンドラ実行
    else 認証失敗
        JS-->>P: UnauthorizedException
        AG-->>R: 401 Unauthorized
    end

カスタムエラーハンドリング

AuthGuardを拡張して、カスタムエラーハンドリングを実装できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// src/auth/guards/jwt-auth.guard.ts
import {
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext) {
    // カスタムロジックを追加可能
    return super.canActivate(context);
  }

  handleRequest(err: any, user: any, info: any) {
    // infoにはJWTエラーの詳細情報が含まれる
    if (err || !user) {
      if (info?.name === 'TokenExpiredError') {
        throw new UnauthorizedException('トークンの有効期限が切れています');
      }
      if (info?.name === 'JsonWebTokenError') {
        throw new UnauthorizedException('無効なトークンです');
      }
      throw err || new UnauthorizedException('認証が必要です');
    }
    return user;
  }
}

AuthModuleの実装

すべてのコンポーネントを統合するモジュールを作成します。

1
2
3
4
// src/auth/constants.ts
export const jwtConstants = {
  secret: process.env.JWT_SECRET || 'your-super-secret-key-change-in-production',
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';
import { LocalStrategy } from './strategies/local.strategy';
import { JwtStrategy } from './strategies/jwt.strategy';
import { jwtConstants } from './constants';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '1h' },
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule {}

PassportModuleのオプション

セッションを使用する場合や、デフォルトの戦略を指定する場合は、PassportModule.register()を使用します。

1
2
3
4
5
// セッションを使用する場合
PassportModule.register({ session: true })

// デフォルトの戦略を指定する場合
PassportModule.register({ defaultStrategy: 'jwt' })

AuthControllerの実装

ログインエンドポイントとプロフィール取得エンドポイントを実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// src/auth/dto/login.dto.ts
import { IsString, IsNotEmpty, MinLength } from 'class-validator';

export class LoginDto {
  @IsString()
  @IsNotEmpty()
  username: string;

  @IsString()
  @IsNotEmpty()
  @MinLength(8)
  password: string;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// src/auth/auth.controller.ts
import {
  Controller,
  Post,
  Get,
  UseGuards,
  Request,
  Body,
  HttpCode,
  HttpStatus,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalAuthGuard } from './guards/local-auth.guard';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { LoginDto } from './dto/login.dto';

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

  @UseGuards(LocalAuthGuard)
  @Post('login')
  @HttpCode(HttpStatus.OK)
  async login(@Request() req, @Body() loginDto: LoginDto) {
    // LocalAuthGuardが認証を処理し、req.userにユーザー情報を設定
    return this.authService.login(req.user);
  }

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

  @UseGuards(JwtAuthGuard)
  @Get('me')
  getCurrentUser(@Request() req) {
    return {
      userId: req.user.userId,
      username: req.user.username,
      roles: req.user.roles,
    };
  }
}

複数の認証戦略を組み合わせる

Passportの強みは、複数の認証戦略を柔軟に組み合わせられることです。

戦略の名前付け

デフォルトでは、各戦略は固有の名前を持ちます(localjwtなど)。カスタム名を付ける場合は、PassportStrategyの第二引数で指定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 名前付き戦略の例
@Injectable()
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromBodyField('refresh_token'),
      ignoreExpiration: false,
      secretOrKey: jwtConstants.refreshSecret,
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}
1
2
3
// 対応するGuard
@Injectable()
export class JwtRefreshGuard extends AuthGuard('jwt-refresh') {}

複数のGuardを連鎖させる

複数のGuardを順番に適用する場合、配列で指定します。

1
2
3
4
5
6
7
// 認証後にロールをチェック
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Get('admin/dashboard')
getAdminDashboard() {
  return { message: '管理者ダッシュボード' };
}

フォールバック認証

複数の認証方式のいずれかで認証を許可する場合、カスタムGuardを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// src/auth/guards/combined-auth.guard.ts
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class CombinedAuthGuard extends AuthGuard(['jwt', 'api-key']) {
  canActivate(context: ExecutionContext) {
    return super.canActivate(context);
  }

  handleRequest(err: any, user: any, info: any, context: ExecutionContext, status?: any) {
    // 最初に成功した戦略のユーザーを返す
    if (user) {
      return user;
    }
    // すべて失敗した場合はエラー
    throw err || new Error('認証に失敗しました');
  }
}

グローバルGuardとPublicエンドポイント

すべてのエンドポイントをデフォルトで保護し、特定のエンドポイントのみ公開する設計を実装します。

1
2
3
4
5
// src/auth/decorators/public.decorator.ts
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// src/auth/guards/jwt-auth.guard.ts
import {
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(private reflector: Reflector) {
    super();
  }

  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) {
      return true;
    }
    return super.canActivate(context);
  }

  handleRequest(err: any, user: any, info: any) {
    if (err || !user) {
      throw err || new UnauthorizedException();
    }
    return user;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// src/app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthModule } from './auth/auth.module';
import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';

@Module({
  imports: [AuthModule],
  providers: [
    {
      provide: APP_GUARD,
      useClass: JwtAuthGuard,
    },
  ],
})
export class AppModule {}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 公開エンドポイントの例
@Controller('auth')
export class AuthController {
  @Public()
  @Post('login')
  @UseGuards(LocalAuthGuard)
  async login(@Request() req) {
    return this.authService.login(req.user);
  }

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

  // 認証必須(グローバルGuardが適用される)
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
  }
}

動作確認

実装した認証システムをcURLで確認します。

ログインテスト

1
2
3
4
# ログインリクエスト
curl -X POST http://localhost:3000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "password123"}'

期待されるレスポンス:

1
2
3
4
5
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

保護されたエンドポイントへのアクセス

1
2
3
4
5
6
# トークンなしでアクセス(401エラー)
curl http://localhost:3000/auth/profile

# トークン付きでアクセス
curl http://localhost:3000/auth/profile \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

期待されるレスポンス:

1
2
3
4
5
{
  "userId": 1,
  "username": "admin",
  "roles": ["admin", "user"]
}

ベストプラクティス

Passport認証を本番環境で運用する際のベストプラクティスをまとめます。

セキュリティに関する推奨事項

項目 推奨事項
JWT秘密鍵 環境変数で管理し、256ビット以上の強力な値を使用
パスワード bcryptでハッシュ化(ソルトラウンド10以上)
トークン有効期限 アクセストークンは短め(15分〜1時間)
HTTPS 本番環境では必ずHTTPS通信を使用
レート制限 ログインエンドポイントにレート制限を適用

構造に関する推奨事項

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 環境変数の使用例
// src/auth/constants.ts
export const jwtConstants = {
  secret: process.env.JWT_SECRET,
  expiresIn: process.env.JWT_EXPIRES_IN || '1h',
  refreshSecret: process.env.JWT_REFRESH_SECRET,
  refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',
};

// バリデーション
if (!jwtConstants.secret) {
  throw new Error('JWT_SECRET環境変数が設定されていません');
}

まとめ

本記事では、NestJSとPassportを使用した認証システムの実装方法を解説しました。

学習した主なポイント:

  • @nestjs/passportパッケージの役割と導入方法
  • PassportStrategyを継承したLocalStrategy、JwtStrategyの実装
  • validate()メソッドによる認証ロジックのカスタマイズ
  • AuthGuardを使用したルート保護
  • 複数の認証戦略を組み合わせる方法
  • グローバルGuardと@Public()デコレータによる柔軟な認可制御

Passportを活用することで、拡張性と保守性に優れた認証基盤を構築できます。OAuth連携やソーシャルログインなど、将来的な機能追加にも柔軟に対応できる設計が可能です。

参考リンク