Laravel Sanctum
はじめに
Laravel Sanctum は、SPA(シングルページアプリケーション)、モバイルアプリケーション、およびシンプルなトークンベースのAPIのための軽量な認証システムを提供します。Sanctumでは、アプリケーションの各ユーザーが自分のアカウントに対して複数のAPIトークンを生成できます。これらのトークンには、トークンが実行できるアクションを指定する権限/スコープを付与できます。
仕組み
Laravel Sanctumは、2つの異なる問題を解決するために存在します。ライブラリを詳しく調べる前に、それぞれについて説明しましょう。
APIトークン
まず、Sanctumは、OAuthの複雑さなしにユーザーにAPIトークンを発行するために使用できるシンプルなパッケージです。この機能は、GitHubやその他のアプリケーションが「パーソナルアクセストークン」を発行することにヒントを得ています。たとえば、アプリケーションの「アカウント設定」に、ユーザーが自分のアカウントのAPIトークンを生成できる画面があるとします。Sanctumを使用して、これらのトークンを生成および管理できます。これらのトークンは通常、非常に長い有効期限(数年)を持っていますが、ユーザーはいつでも手動で取り消すことができます。
Laravel Sanctumは、ユーザーのAPIトークンを単一のデータベーステーブルに格納し、有効なAPIトークンを含む必要があるAuthorization
ヘッダーを介して着信HTTPリクエストを認証することにより、この機能を提供します。
SPA認証
次に、Sanctumは、Laravel対応APIと通信する必要があるシングルページアプリケーション(SPA)を認証するための簡単な方法を提供するために存在します。これらのSPAは、Laravelアプリケーションと同じリポジトリに存在する場合も、Next.jsやNuxtなどを使用して作成されたSPAなど、完全に別個のリポジトリにある場合もあります。
この機能の場合、Sanctumはどの種類のトークンも使用しません。代わりに、SanctumはLaravelの組み込みのCookieベースのセッション認証サービスを使用します。通常、Sanctumはこれを達成するためにLaravelのweb
認証ガードを使用します。これにより、CSRF保護、セッション認証の利点に加えて、XSSを介した認証資格情報の漏洩に対する保護が提供されます。
Sanctumは、着信リクエストが独自のSPAフロントエンドから発信された場合にのみ、Cookieを使用して認証を試みます。Sanctumは着信HTTPリクエストを調べるときに、最初に認証Cookieを確認し、存在しない場合は、有効なAPIトークンについてAuthorization
ヘッダーを調べます。
APIトークン認証のみ、またはSPA認証のみにSanctumを使用することは完全に問題ありません。Sanctumを使用するからといって、提供される両方の機能を使用する必要があるわけではありません。
インストール
install:api
Artisanコマンドを使用してLaravel Sanctumをインストールできます。
php artisan install:api
次に、Sanctumを使用してSPAを認証する予定がある場合は、このドキュメントのSPA認証セクションを参照してください。
設定
デフォルトモデルの上書き
通常は必要ありませんが、Sanctumで内部的に使用されるPersonalAccessToken
モデルを拡張できます。
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken; class PersonalAccessToken extends SanctumPersonalAccessToken{ // ...}
次に、Sanctumによって提供されるusePersonalAccessTokenModel
メソッドを使用して、カスタムモデルを使用するようにSanctumに指示できます。通常、アプリケーションのAppServiceProvider
ファイルのboot
メソッドでこのメソッドを呼び出す必要があります。
use App\Models\Sanctum\PersonalAccessToken;use Laravel\Sanctum\Sanctum; /** * Bootstrap any application services. */public function boot(): void{ Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);}
APIトークン認証
独自のファーストパーティSPAの認証にAPIトークンを使用しないでください。代わりに、Sanctumの組み込みのSPA認証機能を使用してください。
APIトークンの発行
Sanctumを使用すると、アプリケーションへのAPIリクエストを認証するために使用できるAPIトークン/パーソナルアクセストークンを発行できます。APIトークンを使用してリクエストを行う場合、トークンはAuthorization
ヘッダーにBearer
トークンとして含める必要があります。
ユーザーのトークンを発行するには、ユーザーモデルでLaravel\Sanctum\HasApiTokens
トレイトを使用する必要があります。
use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable{ use HasApiTokens, HasFactory, Notifiable;}
トークンを発行するには、createToken
メソッドを使用できます。createToken
メソッドはLaravel\Sanctum\NewAccessToken
インスタンスを返します。APIトークンはデータベースに格納される前にSHA-256ハッシュを使用してハッシュされますが、NewAccessToken
インスタンスのplainTextToken
プロパティを使用してトークンのプレーンテキスト値にアクセスできます。この値は、トークンが作成された直後にユーザーに表示する必要があります。
use Illuminate\Http\Request; Route::post('/tokens/create', function (Request $request) { $token = $request->user()->createToken($request->token_name); return ['token' => $token->plainTextToken];});
HasApiTokens
トレイトによって提供されるtokens
Eloquentリレーションシップを使用して、ユーザーのすべてのトークンにアクセスできます。
foreach ($user->tokens as $token) { // ...}
トークンの権限
Sanctumを使用すると、トークンに「権限」を割り当てることができます。権限は、OAuthの「スコープ」と同様の役割を果たします。createToken
メソッドの第2引数として、文字列権限の配列を渡すことができます。
return $user->createToken('token-name', ['server:update'])->plainTextToken;
Sanctumによって認証された着信リクエストを処理する場合、tokenCan
メソッドを使用して、トークンに特定の権限が付与されているかどうかを確認できます。
if ($user->tokenCan('server:update')) { // ...}
トークンの権限ミドルウェア
Sanctumには、着信リクエストが、特定の権限が付与されたトークンで認証されていることを確認するために使用できる2つのミドルウェアも含まれています。開始するには、アプリケーションのbootstrap/app.php
ファイルで次のミドルウェアエイリアスを定義します。
use Laravel\Sanctum\Http\Middleware\CheckAbilities;use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility; ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'abilities' => CheckAbilities::class, 'ability' => CheckForAnyAbility::class, ]);})
abilities
ミドルウェアは、着信リクエストのトークンにリストされているすべての権限が付与されていることを確認するために、ルートに割り当てることができます。
Route::get('/orders', function () { // Token has both "check-status" and "place-orders" abilities...})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);
ability
ミドルウェアは、着信リクエストのトークンにリストされている権限の少なくとも1つが付与されていることを確認するために、ルートに割り当てることができます。
Route::get('/orders', function () { // Token has the "check-status" or "place-orders" ability...})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);
ファーストパーティUIから開始されたリクエスト
便宜上、着信認証リクエストがファーストパーティのSPAからのものであり、Sanctumの組み込みのSPA認証を使用している場合、tokenCan
メソッドは常にtrue
を返します。
ただし、これは必ずしもアプリケーションがユーザーにアクションの実行を許可する必要があるという意味ではありません。通常、アプリケーションの承認ポリシーは、トークンにアクションの実行権限が付与されているかどうかを確認し、ユーザーインスタンス自体がアクションの実行を許可されるべきかどうかを確認します。
たとえば、サーバーを管理するアプリケーションを想像すると、これは、トークンがサーバーの更新を承認されており、サーバーがユーザーに属していることを確認することを意味する可能性があります。
return $request->user()->id === $server->user_id && $request->user()->tokenCan('server:update')
最初に、ファーストパーティのUIから開始されたリクエストに対してtokenCan
メソッドを呼び出し、常にtrue
を返すことを許可するのは奇妙に思えるかもしれませんが、APIトークンが常に利用可能であり、tokenCan
メソッドで検査できることを常に想定できるのは便利です。このアプローチをとることで、アプリケーションのUIからリクエストがトリガーされたか、APIのサードパーティのコンシューマによって開始されたかに関わらず、アプリケーションの承認ポリシー内で常にtokenCan
メソッドを呼び出すことができます。
ルートの保護
すべての着信リクエストを認証する必要があるようにルートを保護するには、routes/web.php
とroutes/api.php
のルートファイル内で、保護されたルートにsanctum
認証ガードをアタッチする必要があります。このガードは、着信リクエストがステートフルなCookie認証リクエストであるか、リクエストがサードパーティからのものである場合は有効なAPIトークンヘッダーが含まれていることを確認します。
アプリケーションのroutes/web.php
ファイル内のルートをsanctum
ガードを使用して認証することをお勧めする理由について疑問に思われるかもしれません。Sanctumは、最初にLaravelの通常のセッション認証Cookieを使用して着信リクエストの認証を試みます。そのCookieが存在しない場合、SanctumはリクエストのAuthorization
ヘッダー内のトークンを使用してリクエストの認証を試みます。さらに、すべてのリクエストをSanctumを使用して認証することで、現在認証されているユーザーインスタンスで常にtokenCan
メソッドを呼び出すことができます。
use Illuminate\Http\Request; Route::get('/user', function (Request $request) { return $request->user();})->middleware('auth:sanctum');
トークンの取り消し
Laravel\Sanctum\HasApiTokens
トレイトによって提供されるtokens
リレーションシップを使用して、データベースからトークンを削除することで、トークンを「取り消す」ことができます。
// Revoke all tokens...$user->tokens()->delete(); // Revoke the token that was used to authenticate the current request...$request->user()->currentAccessToken()->delete(); // Revoke a specific token...$user->tokens()->where('id', $tokenId)->delete();
トークンの有効期限
デフォルトでは、Sanctumトークンは期限切れになることはなく、トークンの取り消しによってのみ無効になります。ただし、アプリケーションのAPIトークンの有効期限を設定する場合は、アプリケーションのsanctum
設定ファイルで定義されているexpiration
設定オプションを使用して行うことができます。この設定オプションは、発行されたトークンが期限切れと見なされるまでの分数を定義します。
'expiration' => 525600,
各トークンの有効期限を個別に指定する場合は、createToken
メソッドの第3引数として有効期限を提供することで行うことができます。
return $user->createToken( 'token-name', ['*'], now()->addWeek())->plainTextToken;
アプリケーションのトークン有効期限を設定した場合は、アプリケーションの期限切れトークンを削除するためのタスクをスケジュールすることもできます。幸いなことに、Sanctumには、これを実現するために使用できるsanctum:prune-expired
Artisanコマンドが含まれています。たとえば、少なくとも24時間期限切れになっているすべての期限切れトークンダベースレコードを削除するスケジュールされたタスクを設定できます。
use Illuminate\Support\Facades\Schedule; Schedule::command('sanctum:prune-expired --hours=24')->daily();
SPA認証
Sanctumは、LaravelベースのAPIと通信する必要があるシングルページアプリケーション(SPA)を認証するための簡単な方法も提供します。これらのSPAは、Laravelアプリケーションと同じリポジトリに存在する場合もあれば、完全に別々のリポジトリに存在する場合もあります。
この機能では、Sanctumはどのような種類のトークンも使用しません。代わりに、SanctumはLaravelの組み込みのCookieベースのセッション認証サービスを使用します。この認証アプローチは、CSRF保護、セッション認証の利点を提供し、XSSによる認証資格情報の漏洩からも保護します。
認証するには、SPAとAPIは同じトップレベルドメインを共有する必要があります。ただし、異なるサブドメインに配置することもできます。さらに、リクエストにAccept: application/json
ヘッダーとReferer
またはOrigin
ヘッダーのいずれかを必ず送信してください。
設定
ファーストパーティドメインの設定
最初に、SPAがリクエストを行うドメインを設定する必要があります。これらのドメインは、sanctum
設定ファイルのstateful
設定オプションを使用して設定できます。この設定は、APIへのリクエストを行う際に、LaravelセッションCookieを使用して「ステートフル」認証を維持するドメインを決定します。
ポート(127.0.0.1:8000
)を含むURL経由でアプリケーションにアクセスする場合は、ポート番号をドメインに含めるようにしてください。
Sanctumミドルウェア
次に、Laravelに、SPAからの着信リクエストがLaravelのセッションCookieを使用して認証できる一方で、サードパーティまたはモバイルアプリケーションからのリクエストはAPIトークンを使用して認証できることを指示する必要があります。これは、アプリケーションのbootstrap/app.php
ファイルでstatefulApi
ミドルウェアメソッドを呼び出すことで簡単に実現できます。
->withMiddleware(function (Middleware $middleware) { $middleware->statefulApi();})
CORSとCookie
別のサブドメインで実行されるSPAからアプリケーションで認証する際に問題が発生する場合は、CORS(Cross-Origin Resource Sharing)またはセッションCookieの設定が誤っている可能性があります。
config/cors.php
設定ファイルはデフォルトでは公開されません。LaravelのCORSオプションをカスタマイズする必要がある場合は、config:publish
Artisanコマンドを使用して、完全なcors
設定ファイルを公開する必要があります。
php artisan config:publish cors
次に、アプリケーションのCORS設定が、値がTrue
のAccess-Control-Allow-Credentials
ヘッダーを返していることを確認する必要があります。これは、アプリケーションのconfig/cors.php
設定ファイル内のsupports_credentials
オプションをtrue
に設定することで実現できます。
さらに、アプリケーションのグローバルaxios
インスタンスでwithCredentials
オプションとwithXSRFToken
オプションを有効にする必要があります。通常、これはresources/js/bootstrap.js
ファイルで行う必要があります。フロントエンドからHTTPリクエストを行うためにAxiosを使用していない場合は、独自のHTTPクライアントで同等の設定を実行する必要があります。
axios.defaults.withCredentials = true;axios.defaults.withXSRFToken = true;
最後に、アプリケーションのセッションCookieドメイン設定がルートドメインの任意のサブドメインをサポートしていることを確認する必要があります。これは、アプリケーションのconfig/session.php
設定ファイル内で、ドメインの前に.
を付けることで実現できます。
'domain' => '.domain.com',
認証
CSRF保護
SPAを認証するには、SPAの「ログイン」ページで最初に/sanctum/csrf-cookie
エンドポイントへのリクエストを行い、アプリケーションのCSRF保護を初期化する必要があります。
axios.get('/sanctum/csrf-cookie').then(response => { // Login...});
このリクエスト中、Laravelは、現在のCSRFトークンを含むXSRF-TOKEN
Cookieを設定します。このトークンは、その後のリクエストでX-XSRF-TOKEN
ヘッダーに渡す必要があります。AxiosやAngular HttpClientなどの一部のHTTPクライアントライブラリは、これを自動的に行います。JavaScript HTTPライブラリが値を設定しない場合は、このルートによって設定されたXSRF-TOKEN
Cookieの値と一致するように、X-XSRF-TOKEN
ヘッダーを手動で設定する必要があります。
ログイン
CSRF保護が初期化されたら、Laravelアプリケーションの/login
ルートにPOST
リクエストを行う必要があります。この/login
ルートは、手動で実装することも、Laravel Fortifyのようなヘッドレス認証パッケージを使用することもできます。
ログインリクエストが成功すると、認証され、アプリケーションのルートへの以降のリクエストは、Laravelアプリケーションがクライアントに発行したセッションCookieを介して自動的に認証されます。さらに、アプリケーションが既に/sanctum/csrf-cookie
ルートへのリクエストを行っているため、JavaScript HTTPクライアントがX-XSRF-TOKEN
ヘッダーにXSRF-TOKEN
Cookieの値を送信する限り、以降のリクエストは自動的にCSRF保護を受けます。
もちろん、アクティビティの不足によりユーザーのセッションが期限切れになった場合、Laravelアプリケーションへの以降のリクエストは、401または419 HTTPエラー応答を受け取る可能性があります。この場合、ユーザーをSPAのログインページにリダイレクトする必要があります。
独自の/login
エンドポイントを作成できますが、標準のLaravelが提供するセッションベースの認証サービスを使用してユーザーを認証するようにする必要があります。通常、これはweb
認証ガードを使用することを意味します。
ルートの保護
すべての着信リクエストを認証する必要があるようにルートを保護するには、routes/api.php
ファイル内のAPIルートにsanctum
認証ガードをアタッチする必要があります。このガードは、着信リクエストがSPAからのステートフル認証リクエストであるか、リクエストがサードパーティからのものである場合は有効なAPIトークンヘッダーが含まれていることを確認します。
use Illuminate\Http\Request; Route::get('/user', function (Request $request) { return $request->user();})->middleware('auth:sanctum');
プライベートブロードキャストチャネルの承認
SPAがプライベート/プレゼンスブロードキャストチャネルで認証する必要がある場合は、アプリケーションのbootstrap/app.php
ファイルに含まれるwithRouting
メソッドからchannels
エントリを削除する必要があります。代わりに、withBroadcasting
メソッドを呼び出して、アプリケーションのブロードキャストルートに適切なミドルウェアを指定できるようにする必要があります。
return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', // ... ) ->withBroadcasting( __DIR__.'/../routes/channels.php', ['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']], )
次に、Pusherの承認リクエストが成功するには、Laravel Echoを初期化するときに、カスタムPusherのauthorizer
を提供する必要があります。これにより、アプリケーションは、クロスドメインリクエストに対して適切に構成されたaxios
インスタンスを使用するようにPusherを設定できます。
window.Echo = new Echo({ broadcaster: "pusher", cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, encrypted: true, key: import.meta.env.VITE_PUSHER_APP_KEY, authorizer: (channel, options) => { return { authorize: (socketId, callback) => { axios.post('/api/broadcasting/auth', { socket_id: socketId, channel_name: channel.name }) .then(response => { callback(false, response.data); }) .catch(error => { callback(true, error); }); } }; },})
モバイルアプリケーション認証
Sanctumトークンを使用して、モバイルアプリケーションのリクエストをAPIで認証することもできます。モバイルアプリケーションリクエストを認証するプロセスは、サードパーティAPIリクエストを認証するプロセスに似ていますが、APIトークンの発行方法には小さな違いがあります。
APIトークンの発行
開始するには、ユーザーのメールアドレス/ユーザー名、パスワード、デバイス名を受け入れるルートを作成し、これらの資格情報を新しいSanctumトークンと交換します。このエンドポイントに与えられた「デバイス名」は情報提供用であり、任意の値にすることができます。一般的に、デバイス名の値は、「NunoのiPhone 12」など、ユーザーが認識できる名前である必要があります。
通常、モバイルアプリケーションの「ログイン」画面からトークンエンドポイントへのリクエストを行います。エンドポイントでは、プレーンテキストのAPIトークンが返され、それをモバイルデバイスに保存して、追加のAPIリクエストを行うことができます。
use App\Models\User;use Illuminate\Http\Request;use Illuminate\Support\Facades\Hash;use Illuminate\Validation\ValidationException; Route::post('/sanctum/token', function (Request $request) { $request->validate([ 'email' => 'required|email', 'password' => 'required', 'device_name' => 'required', ]); $user = User::where('email', $request->email)->first(); if (! $user || ! Hash::check($request->password, $user->password)) { throw ValidationException::withMessages([ 'email' => ['The provided credentials are incorrect.'], ]); } return $user->createToken($request->device_name)->plainTextToken;});
モバイルアプリケーションがトークンを使用してアプリケーションへのAPIリクエストを行う場合、トークンをAuthorization
ヘッダーにBearer
トークンとして渡す必要があります。
モバイルアプリケーションのトークンを発行する場合は、トークンの機能を指定することもできます。
ルートの保護
既に説明したように、ルートにsanctum
認証ガードをアタッチすることで、すべての着信リクエストを認証する必要があるようにルートを保護できます。
Route::get('/user', function (Request $request) { return $request->user();})->middleware('auth:sanctum');
トークンの取り消し
ユーザーがモバイルデバイスに発行されたAPIトークンを取り消すことができるようにするには、それらを名前で一覧表示し、「取り消し」ボタンとともに、WebアプリケーションのUIの「アカウント設定」部分に表示できます。ユーザーが「取り消し」ボタンをクリックすると、データベースからトークンを削除できます。Laravel\Sanctum\HasApiTokens
トレイトによって提供されるtokens
リレーションシップを介して、ユーザーのAPIトークンにアクセスできることを忘れないでください。
// Revoke all tokens...$user->tokens()->delete(); // Revoke a specific token...$user->tokens()->where('id', $tokenId)->delete();
テスト
テスト中は、Sanctum::actingAs
メソッドを使用してユーザーを認証し、トークンに付与する機能を指定できます。
use App\Models\User;use Laravel\Sanctum\Sanctum; test('task list can be retrieved', function () { Sanctum::actingAs( User::factory()->create(), ['view-tasks'] ); $response = $this->get('/api/task'); $response->assertOk();});
use App\Models\User;use Laravel\Sanctum\Sanctum; public function test_task_list_can_be_retrieved(): void{ Sanctum::actingAs( User::factory()->create(), ['view-tasks'] ); $response = $this->get('/api/task'); $response->assertOk();}
トークンにすべての機能を付与する場合は、actingAs
メソッドに提供される機能リストに*
を含める必要があります。
Sanctum::actingAs( User::factory()->create(), ['*']);