SupabaseとNestJSで認証機能を実装する方法について
NestJS と Supabaseで認証機能を実装する方法
NestJS は、拡張性と生産性を重視した Node.js のフレームワークです。一方、Supabase はオープンソースの Firebase 代替であり、認証、データベース、ストレージなどのバックエンドサービスを提供します。この2つを組み合わせることで、強力でスケーラブルな認証システムを構築できます。
以下に、NestJS と Supabase を使用して認証機能を実装する手順を詳しく説明します。
前提条件
- Node.js と npm がインストールされていること
- TypeScript と NestJS の基本的な知識
- Supabase アカウント(supabase.io でサインアップできます)
ステップ1: Supabase プロジェクトのセットアップ
-
Supabase アカウントの作成
- Supabase の公式サイトでアカウントを作成します。
- 新しいプロジェクトを作成し、プロジェクトの URL と API キー(
anon
キーとservice_role
キー)を取得します。
-
認証プロバイダーの設定
- Supabase ダッシュボードの「Authentication」タブに移動します。
- 必要な認証プロバイダー(Email、Google、GitHub など)を有効にします。
- メールテンプレートやリダイレクト URL など、追加の設定を行います。
ステップ2: NestJS プロジェクトのセットアップ
-
新しい NestJS アプリケーションの作成
npm i -g @nestjs/cli nest new my-auth-app cd my-auth-app
-
Supabase クライアントライブラリのインストール
npm install @supabase/supabase-js
ステップ3: Supabase の設定
-
Supabase モジュールの作成
-
Supabase に関連する機能をカプセル化するための新しいモジュールを作成します。
nest g module supabase
-
-
環境変数の設定
-
プロジェクトのルートに
.env
ファイルを作成し、以下を追加します。SUPABASE_URL=your_supabase_url SUPABASE_ANON_KEY=your_supabase_anon_key SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
-
dotenv
パッケージをインストールして環境変数をロードします。npm install dotenv
-
main.ts
を更新して環境変数をロードします。import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as dotenv from 'dotenv'; async function bootstrap() { dotenv.config(); const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();
-
-
Supabase クライアントの初期化
-
SupabaseModule
内で Supabase クライアントを初期化するプロバイダーを作成します。// supabase.module.ts import { Module, Global } from '@nestjs/common'; import { createClient, SupabaseClient } from '@supabase/supabase-js'; @Global() @Module({ providers: [ { provide: 'SUPABASE_CLIENT', useFactory: (): SupabaseClient => { const supabaseUrl = process.env.SUPABASE_URL; const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY; return createClient(supabaseUrl, supabaseServiceRoleKey); }, }, ], exports: ['SUPABASE_CLIENT'], }) export class SupabaseModule {}
-
ステップ4: 認証ロジックの実装
-
Auth サービスの作成
-
認証ロジックを処理するための新しいサービスを生成します。
nest g service auth
-
Supabase クライアントを
AuthService
に注入します。// auth.service.ts import { Injectable, Inject } from '@nestjs/common'; import { SupabaseClient } from '@supabase/supabase-js'; @Injectable() export class AuthService { constructor( @Inject('SUPABASE_CLIENT') private readonly supabase: SupabaseClient, ) {} async signUp(email: string, password: string) { const { user, error } = await this.supabase.auth.signUp({ email, password, }); if (error) { throw new Error(error.message); } return user; } async signIn(email: string, password: string) { const { user, error } = await this.supabase.auth.signInWithPassword({ email, password, }); if (error) { throw new Error(error.message); } return user; } // その他のメソッド(signOut、パスワードリセットなど) }
-
-
Auth コントローラーの作成
-
認証ルートを処理するための新しいコントローラーを生成します。
nest g controller auth
-
auth.controller.ts
にルートを実装します。// auth.controller.ts import { Controller, Post, Body } from '@nestjs/common'; import { AuthService } from './auth.service'; @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @Post('signup') async signUp( @Body('email') email: string, @Body('password') password: string, ) { const user = await this.authService.signUp(email, password); return { user }; } @Post('signin') async signIn( @Body('email') email: string, @Body('password') password: string, ) { const user = await this.authService.signIn(email, password); return { user }; } // その他のルート(signOut など) }
-
-
認証トークンの処理
- Supabase は JWT トークンを使用して認証します。
-
signIn
メソッドのレスポンスからaccess_token
とrefresh_token
を取得できます。
-
Auth ガードの実装
-
認証が必要なルートを保護するためのガードを作成します。
nest g guard auth
-
ガードを実装します。
// auth.guard.ts import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { SupabaseClient } from '@supabase/supabase-js'; import { Inject } from '@nestjs/common'; @Injectable() export class AuthGuard implements CanActivate { constructor( @Inject('SUPABASE_CLIENT') private readonly supabase: SupabaseClient, ) {} async canActivate(context: ExecutionContext): Promise<boolean> { const request = context.switchToHttp().getRequest(); const accessToken = request.headers['authorization']?.split(' ')[1]; if (!accessToken) { return false; } const { data: user, error } = await this.supabase.auth.getUser( accessToken, ); if (error || !user) { return false; } request.user = user; return true; } }
-
ガードを保護したいルートに適用します。
// protected.controller.ts import { Controller, Get, UseGuards } from '@nestjs/common'; import { AuthGuard } from './auth.guard'; @Controller('protected') @UseGuards(AuthGuard) export class ProtectedController { @Get() getProtectedResource() { return { data: 'This is protected data' }; } }
-
ステップ5: 実装のテスト
-
サーバーの起動
npm run start
-
API エンドポイントのテスト
-
サインアップ
POST http://localhost:3000/auth/signup Content-Type: application/json { "email": "user@example.com", "password": "password123" }
-
サインイン
POST http://localhost:3000/auth/signin Content-Type: application/json { "email": "user@example.com", "password": "password123" }
-
保護されたルートへのアクセス
GET http://localhost:3000/protected Authorization: Bearer YOUR_ACCESS_TOKEN
-
ステップ6: 追加の考慮事項
-
トークンのリフレッシュ
-
refresh_token
を使用してトークンのリフレッシュ機能を実装します。
-
-
ロールベースのアクセス制御(RBAC)
- Supabase のポリシーとロールを使用して、ユーザーのアクセスレベルを制御できます。
-
エラーハンドリング
- 適切なエラーハンドリングとレスポンスを実装します。
-
セキュリティ
- トークンを安全に保存し、プロダクション環境では HTTPS を使用します。
まとめ
Supabase と NestJS を統合することで、効率的に認証機能を実装できます。Supabase がユーザ管理の重荷を軽減し、NestJS が堅牢な API を構築するためのフレームワークを提供します。
参考・引用
NestJS、Prismaで認証機能を作成