Contents
NestJS GraphQL 認証 実装 手順:Code Firstアプローチで最新パターンを実現する
GraphQL APIの認証構築に悩む開発者向けに、NestJSとFirebase Authenticationを組み合わせたJWT認証フローの実装手順を解説します。現在の技術動向(2023年時点)に基づき、Code Firstアプローチ×Passport戦略×Firebase連携のパターンを体系的に紹介し、プロジェクトへの適用がスムーズになるよう設計しています。
NestJSとGraphQLのプロジェクト初期設定
NestJSでGraphQLサーバーを開発する際には、最初にプロジェクト構成とモジュール導入を確実に行う必要があります。Code Firstアプローチでは、TypeScriptの型定義がGraphQLスキーマに自動変換されるため、開発効率が高まります。
NestJS CLIによるプロジェクト作成
NestJS CLIを使用してプロジェクトを生成し、GraphQLモジュール導入を選択します。
-
NestJS CLIでプロジェクト生成
bash
npm i -g @nestjs/cli
nest new graphql-auth-demo -
必要ライブラリのインストール
GraphQLサーバー構築に必要なモジュールを導入します。
bash
npm install @nestjs/graphql graphql-tools apollo-server-express
Code First vs Schema Firstの選定基準
GraphQL API設計において、Code FirstとSchema Firstの選択は開発スタイルやチーム規模に大きく影響します。それぞれの特徴を比較し、プロジェクト要件に合った方法を選定してください。
| 項目 | Code First | Schema First |
|---|---|---|
| 定義方式 | TypeScriptで型定義(@InputType, @ObjectType) |
GraphQLスキーマファイル(.graphql) |
| 開発効率 | 高(TypeScriptコードと同期) | 中〜低(別途スキーマ管理必要) |
| ツール連携 | Prismaなどと連携しやすい | 限定的 |
| おすすめ対象 | 型駆動開発を重視するプロジェクト | GraphQL独自のスキーマ設計が必要な場合 |
Code Firstは、TypeScriptとの親和性が高く、GraphQLスキーマと型定義の不一致を防げます。
JWT認証フローの構築(Passport・AuthGuard)
JWT認証では、Passport戦略によるトークン検証とAuthGuardでリクエストフィルタリングを行います。以下に手順を示します。
Passport戦略のカスタマイズ手順
-
Passport-JWTモジュール導入
bash
npm install passport-jwt @nestjs/passport -
JWT戦略の定義
パスワード認証用にJwtStrategyクラスを作成し、以下のように実装します。
typescript
import { Strategy } from 'passport-jwt';
import { ExtractJwt } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET, // 環境変数からシークレット読み込み
});
}
|
1 2 3 4 |
validate(payload: any) { return { userId: payload.sub, username: payload.username }; } |
}
- AuthGuardの使用
リゾルバに@UseGuards(JwtAuthGuard)を追加することで、認証チェックが自動で実行されます。
Firebase Authenticationとの連携
Firebase Authenticationは、フロントエンドでのトークン取得とバックエンドでの検証を組み合わせた柔軟な認証フローを提供します。以下の手順で導入できます。
Firebase SDKの導入手順
-
Firebase Admin SDKインストール
bash
npm install firebase-admin -
Firebase初期化設定
firebaseConfig.tsファイルにプロジェクト情報を配置し、初期化します。
typescript
import * as admin from 'firebase-admin';
const serviceAccount = require('./path/to/service-account.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
- IDトークンの検証処理
パスワード認証と同様に、FirebaseAuthGuardを作成し、以下のように検証ロジックを実装します。
typescript
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class FirebaseAuthGuard extends AuthGuard('firebase') {
// 特にオーバーライド不要
}
Firebase Authenticationを使うことで、セキュリティリスクを最小限に抑えつつ、フロントエンドの認証フローを柔軟に設計できます。
GraphQLエンドポイントの認証保護
GraphQL APIでは、ResolverレベルとSchemaレベルでのアクセス制御を組み合わせることで、細粒度なセキュリティを実現します。
Resolverレベルでのアクセス制御
- AuthGuardデコレータの使用
リゾルバに@UseGuards(JwtAuthGuard)や@UseGuards(FirebaseAuthGuard)を付与することで、認証が必要なエンドポイントを指定できます。
typescript
@Resolver()
export class UserResolver {
@Query()
@UseGuards(JwtAuthGuard)
getUser(@Args('id') id: string): Promise<User> {
return this.userService.findOne(id);
}
}
- Roleベースのアクセス制御(RBAC)
ユーザー権限をチェックするには、@Roles()デコレータとカスタムガードで実装します。
Schemaレベルでの制御(例:Field Directive)
Schemaレベルでは、GraphQLスキーマに独自のDirectiveを定義し、特定のフィールドへのアクセスを制限できます。
@authdirectiveを使って、isAuthenticatedなどの条件を適用。@role(role: "admin")のようにロールベースでフィールドを保護。
トークン検証ロジックのカスタマイズ
JWTトークンをより柔軟に扱うには、ペイロードの拡張や検証フックのカスタマイズが効果的です。以下に具体的な例を示します。
ペイロードの拡張方法
- 独自フィールドの追加
payloadオブジェクトに任意のフィールド(例:role,tenantId)を含めます。
typescript
{
userId: '123',
username: 'hoge',
role: 'admin'
}
- セキュリティ強化
検証時にペイロードの有効性を確認する関数を実装します。
typescript
validate(payload: any) {
if (!payload.role || !['user', 'admin'].includes(payload.role)) {
throw new UnauthorizedException('不正なロール');
}
return payload;
}
FirebaseAuthGuardの検証ロジック改善例
Firebaseトークンをより正確に検証するには、FirebaseStrategyをカスタマイズし、以下のように実装します。
|
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 |
import { Strategy } from 'passport-jwt'; import { ExtractJwt } from 'passport-jwt'; import * as admin from 'firebase-admin'; @Injectable() export class FirebaseStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: 'your-secret-key', // ローカル検証用のシークレット(実際には不要) }); } async validate(payload: any) { const token = payload.token; try { const decodedToken = await admin.auth().verifyIdToken(token); return { uid: decodedToken.uid, email: decodedToken.email }; } catch (error) { throw new UnauthorizedException('トークンの検証に失敗しました'); } } } |
結論と今後の展望
本記事では、NestJS GraphQL APIにおける認証フローの実装手順を以下の6ステップで解説しました。
- プロジェクト初期設定:CLIとモジュール導入
- Code First/SF選定:開発スタイルに応じたアプローチの選択
- JWT認証構築:Passport戦略でトークン検証を実装
- Firebase連携:フロントとバックエンドでの安全な認証フロー設計
- GraphQL保護:ResolverとSchemaレベルのアクセス制御
- トークンカスタマイズ:ペイロード拡張と検証フックの追加
実際のプロジェクトに応じて、柔軟にこれらの手法を組み合わせることで、信頼性の高いGraphQL APIを構築できます。実装中に詰まるポイントがあればコメント欄で質問してください。