はじめに

Laravel Sanctum は、SPA(シングルページアプリケーション)、モバイルアプリケーション、およびシンプルなトークンベースのAPIのための軽量な認証システムを提供します。Sanctumは、アプリケーションの各ユーザーが自分のアカウントのために複数のAPIトークンを生成できるようにします。これらのトークンには、トークンが実行できるアクションを指定する能力/スコープが付与される場合があります。

動作原理

Laravel Sanctumは、2つの異なる問題を解決するために存在します。それぞれについて詳しく掘り下げる前に説明しましょう。

APIトークン

まず、SanctumはOAuthの複雑さなしにユーザーにAPIトークンを発行するために使用できるシンプルなパッケージです。この機能は、GitHubや他のアプリケーションが発行する「個人アクセストークン」に触発されています。たとえば、アプリケーションの「アカウント設定」に、ユーザーが自分のアカウントのためにAPIトークンを生成できる画面があると想像してください。Sanctumを使用してこれらのトークンを生成および管理できます。これらのトークンは通常、非常に長い有効期限(数年)を持ちますが、ユーザーはいつでも手動で取り消すことができます。

Laravel Sanctumは、ユーザーAPIトークンを単一のデータベーステーブルに保存し、Authorizationヘッダーを介して受信HTTPリクエストを認証することによってこの機能を提供します。このヘッダーには有効なAPIトークンが含まれている必要があります。

SPA認証

次に、Sanctumは、LaravelパワードAPIと通信する必要があるシングルページアプリケーション(SPA)を認証するためのシンプルな方法を提供するために存在します。これらのSPAは、Laravelアプリケーションと同じリポジトリに存在する場合もあれば、Next.jsやNuxtを使用して作成された完全に別のリポジトリである場合もあります。

この機能では、Sanctumはトークンを使用しません。代わりに、SanctumはLaravelの組み込みのクッキーに基づくセッション認証サービスを使用します。通常、SanctumはLaravelのweb認証ガードを利用してこれを実現します。これにより、CSRF保護、セッション認証の利点が提供され、XSSを介した認証資格情報の漏洩から保護されます。

Sanctumは、受信リクエストが自分のSPAフロントエンドから発信された場合にのみ、クッキーを使用して認証を試みます。Sanctumが受信HTTPリクエストを調べると、最初に認証クッキーが存在するかどうかを確認し、存在しない場合はAuthorizationヘッダーを調べて有効なAPIトークンを確認します。

SanctumをAPIトークン認証のみに使用することも、SPA認証のみに使用することも完全に問題ありません。Sanctumを使用しているからといって、提供される両方の機能を使用する必要はありません。

インストール

Laravel Sanctumは、install:api Artisanコマンドを介してインストールできます:

  1. php artisan install:api

次に、SPAを認証するためにSanctumを利用する予定がある場合は、このドキュメントのSPA認証セクションを参照してください。

設定

デフォルトモデルのオーバーライド

通常は必要ありませんが、Sanctumが内部で使用するPersonalAccessTokenモデルを拡張することは自由です:

  1. use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
  2. class PersonalAccessToken extends SanctumPersonalAccessToken
  3. {
  4. // ...
  5. }

次に、Sanctumが提供するusePersonalAccessTokenModelメソッドを介してカスタムモデルを使用するように指示できます。通常、このメソッドはアプリケーションのAppServiceProviderファイルのbootメソッド内で呼び出す必要があります:

  1. use App\Models\Sanctum\PersonalAccessToken;
  2. use Laravel\Sanctum\Sanctum;
  3. /**
  4. * Bootstrap any application services.
  5. */
  6. public function boot(): void
  7. {
  8. Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
  9. }

APIトークン認証

自分のファーストパーティSPAを認証するためにAPIトークンを使用すべきではありません。代わりに、Sanctumの組み込みのSPA認証機能を使用してください。

APIトークンの発行

Sanctumを使用すると、アプリケーションへのAPIリクエストを認証するために使用できるAPIトークン/個人アクセストークンを発行できます。APIトークンを使用してリクエストを行う場合、トークンはAuthorizationヘッダーにBearerトークンとして含める必要があります。

ユーザーのトークンを発行するには、UserモデルがLaravel\Sanctum\HasApiTokensトレイトを使用する必要があります:

  1. use Laravel\Sanctum\HasApiTokens;
  2. class User extends Authenticatable
  3. {
  4. use HasApiTokens, HasFactory, Notifiable;
  5. }

トークンを発行するには、createTokenメソッドを使用できます。createTokenメソッドはLaravel\Sanctum\NewAccessTokenインスタンスを返します。APIトークンはSHA-256ハッシュを使用してハッシュ化され、データベースに保存されますが、NewAccessTokenインスタンスのplainTextTokenプロパティを使用してトークンのプレーンテキスト値にアクセスできます。この値は、トークンが作成された直後にユーザーに表示する必要があります:

  1. use Illuminate\Http\Request;
  2. Route::post('/tokens/create', function (Request $request) {
  3. $token = $request->user()->createToken($request->token_name);
  4. return ['token' => $token->plainTextToken];
  5. });

ユーザーのすべてのトークンには、tokens Eloquentリレーションシップを使用してアクセスできます。これはHasApiTokensトレイトによって提供されます:

  1. foreach ($user->tokens as $token) {
  2. // ...
  3. }

トークンの能力

Sanctumは、トークンに「能力」を割り当てることを許可します。能力はOAuthの「スコープ」と似た目的を持ちます。createTokenメソッドに2番目の引数として文字列の能力の配列を渡すことができます:

  1. return $user->createToken('token-name', ['server:update'])->plainTextToken;

Sanctumによって認証された受信リクエストを処理する際、tokenCanメソッドを使用してトークンが特定の能力を持っているかどうかを判断できます:

  1. if ($user->tokenCan('server:update')) {
  2. // ...
  3. }

トークン能力ミドルウェア

Sanctumには、受信リクエストが特定の能力が付与されたトークンで認証されていることを確認するために使用できる2つのミドルウェアが含まれています。始めるには、アプリケーションのbootstrap/app.phpファイルに次のミドルウェアエイリアスを定義します:

  1. use Laravel\Sanctum\Http\Middleware\CheckAbilities;
  2. use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
  3. ->withMiddleware(function (Middleware $middleware) {
  4. $middleware->alias([
  5. 'abilities' => CheckAbilities::class,
  6. 'ability' => CheckForAnyAbility::class,
  7. ]);
  8. })
  1. ``````php
  2. Route::get('/orders', function () {
  3. // Token has both "check-status" and "place-orders" abilities...
  4. })->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);
  5. `
  1. ``````php
  2. Route::get('/orders', function () {
  3. // Token has the "check-status" or "place-orders" ability...
  4. })->middleware(['auth:sanctum', 'ability:check-status,place-orders']);
  5. `

ファーストパーティUIによるリクエスト

便利なことに、tokenCanメソッドは、受信認証リクエストがファーストパーティSPAからのものであり、Sanctumの組み込みのSPA認証を使用している場合、常にtrueを返します。

ただし、これは必ずしもアプリケーションがユーザーにアクションを実行させることを許可する必要があることを意味するわけではありません。通常、アプリケーションの認可ポリシーが、トークンが能力を実行する権限を付与されているかどうかを判断し、ユーザーインスタンス自体がアクションを実行することを許可されているかどうかを確認します。

たとえば、サーバーを管理するアプリケーションを想像すると、これはトークンがサーバーを更新する権限を持っていることを確認し、かつサーバーがユーザーに属していることを確認することを意味するかもしれません:

  1. return $request->user()->id === $server->user_id &&
  2. $request->user()->tokenCan('server:update')

最初は、tokenCanメソッドを呼び出してファーストパーティUIによるリクエストに対して常にtrueを返すことは奇妙に思えるかもしれませんが、APIトークンが常に利用可能であり、tokenCanメソッドを介して検査できると仮定できることは便利です。このアプローチを取ることで、アプリケーションの認可ポリシー内でtokenCanメソッドを常に呼び出すことができ、リクエストがアプリケーションのUIからトリガーされたのか、APIのサードパーティ消費者の1人によって開始されたのかを心配する必要がありません。

ルートの保護

すべての受信リクエストが認証されるようにルートを保護するには、sanctum認証ガードをroutes/web.phpおよびroutes/api.phpルートファイル内の保護されたルートに添付する必要があります。このガードは、受信リクエストが状態を持つ、クッキー認証されたリクエストとして認証されるか、リクエストがサードパーティからのものである場合は有効なAPIトークンヘッダーを含むことを保証します。

なぜアプリケーションのroutes/web.phpファイル内でsanctumガードを使用してルートを認証することを提案するのか疑問に思うかもしれません。Sanctumは、最初にLaravelの通常のセッション認証クッキーを使用して受信リクエストを認証しようとします。そのクッキーが存在しない場合、SanctumはリクエストのAuthorizationヘッダー内のトークンを使用してリクエストを認証しようとします。さらに、Sanctumを使用してすべてのリクエストを認証することで、現在認証されているユーザーインスタンスに対してtokenCanメソッドを常に呼び出すことができることが保証されます:

  1. use Illuminate\Http\Request;
  2. Route::get('/user', function (Request $request) {
  3. return $request->user();
  4. })->middleware('auth:sanctum');

トークンの取り消し

データベースからトークンを削除することで、トークンを「取り消す」ことができます。これは、Laravel\Sanctum\HasApiTokensトレイトによって提供されるtokensリレーションシップを使用して行います:

  1. // Revoke all tokens...
  2. $user->tokens()->delete();
  3. // Revoke the token that was used to authenticate the current request...
  4. $request->user()->currentAccessToken()->delete();
  5. // Revoke a specific token...
  6. $user->tokens()->where('id', $tokenId)->delete();

トークンの有効期限

デフォルトでは、Sanctumトークンは決して期限切れにならず、トークンを取り消すことによってのみ無効にできます。ただし、アプリケーションのAPIトークンに有効期限を設定したい場合は、アプリケーションのsanctum設定ファイルで定義されたexpiration設定オプションを介して行うことができます。この設定オプションは、発行されたトークンが期限切れと見なされるまでの分数を定義します:

  1. 'expiration' => 525600,

各トークンの有効期限を独立して指定したい場合は、createTokenメソッドに3番目の引数として有効期限を提供することで行うことができます:

  1. return $user->createToken(
  2. 'token-name', ['*'], now()->addWeek()
  3. )->plainTextToken;

アプリケーションのトークン有効期限を設定した場合、アプリケーションの期限切れトークンをプルーニングするためにタスクをスケジュールすることも検討するかもしれません。幸いなことに、Sanctumにはこれを実行するために使用できるsanctum:prune-expired Artisanコマンドが含まれています。たとえば、少なくとも24時間経過したすべての期限切れトークンのデータベースレコードを削除するためにスケジュールされたタスクを設定できます:

  1. use Illuminate\Support\Facades\Schedule;
  2. Schedule::command('sanctum:prune-expired --hours=24')->daily();

SPA認証

Sanctumは、LaravelパワードAPIと通信する必要があるシングルページアプリケーション(SPA)を認証するためのシンプルな方法を提供するために存在します。これらのSPAは、Laravelアプリケーションと同じリポジトリに存在する場合もあれば、完全に別のリポジトリに存在する場合もあります。

この機能では、Sanctumはトークンを使用しません。代わりに、SanctumはLaravelの組み込みのクッキーに基づくセッション認証サービスを使用します。この認証アプローチは、CSRF保護、セッション認証の利点を提供し、XSSを介した認証資格情報の漏洩から保護します。

認証するためには、SPAとAPIが同じトップレベルドメインを共有する必要があります。ただし、異なるサブドメインに配置することはできます。さらに、リクエストにAccept: application/jsonヘッダーとRefererまたはOriginヘッダーのいずれかを送信することを確認する必要があります。

設定

ファーストパーティドメインの設定

まず、SPAがリクエストを行うドメインを設定する必要があります。これらのドメインは、アプリケーションのsanctum設定ファイル内のstateful設定オプションを使用して設定できます。この設定は、APIにリクエストを行う際にLaravelセッションクッキーを使用して「状態を持つ」認証を維持するドメインを決定します。

ポートを含むURLを介してアプリケーションにアクセスしている場合(127.0.0.1:8000)、ドメインとともにポート番号を含めることを確認してください。

Sanctumミドルウェア

次に、SPAからの受信リクエストがLaravelのセッションクッキーを使用して認証できるようにLaravelに指示し、サードパーティやモバイルアプリケーションからのリクエストがAPIトークンを使用して認証できるようにします。これは、アプリケーションのbootstrap/app.phpファイル内でstatefulApiミドルウェアメソッドを呼び出すことで簡単に実現できます:

  1. ->withMiddleware(function (Middleware $middleware) {
  2. $middleware->statefulApi();
  3. })

CORSとクッキー

別のサブドメインで実行されるSPAからアプリケーションに認証する際に問題が発生している場合、CORS(クロスオリジンリソースシェアリング)またはセッションクッキー設定が誤って構成されている可能性があります。

config/cors.php設定ファイルはデフォルトでは公開されていません。LaravelのCORSオプションをカスタマイズする必要がある場合は、config:publish Artisanコマンドを使用して完全なcors設定ファイルを公開する必要があります:

  1. php artisan config:publish cors

次に、アプリケーションのCORS設定がAccess-Control-Allow-CredentialsヘッダーをTrueの値で返すことを確認する必要があります。これは、アプリケーションのconfig/cors.php設定ファイル内でsupports_credentialsオプションをtrueに設定することで実現できます。

さらに、アプリケーションのグローバルaxiosインスタンスでwithCredentialsおよびwithXSRFTokenオプションを有効にする必要があります。通常、これはresources/js/bootstrap.jsファイルで実行されます。フロントエンドからHTTPリクエストを行うためにAxiosを使用していない場合は、自分のHTTPクライアントで同等の設定を行う必要があります:

  1. axios.defaults.withCredentials = true;
  2. axios.defaults.withXSRFToken = true;

最後に、アプリケーションのセッションクッキードメイン設定がルートドメインの任意のサブドメインをサポートしていることを確認する必要があります。これは、アプリケーションのconfig/session.php設定ファイル内でドメインを先頭に.を付けることで実現できます:

  1. 'domain' => '.domain.com',

認証

CSRF保護

SPAを認証するには、SPAの「ログイン」ページが最初に/sanctum/csrf-cookieエンドポイントにリクエストを行い、アプリケーションのCSRF保護を初期化する必要があります:

  1. axios.get('/sanctum/csrf-cookie').then(response => {
  2. // Login...
  3. });

このリクエスト中に、Laravelは現在のCSRFトークンを含むXSRF-TOKENクッキーを設定します。このトークンは、その後のリクエストでX-XSRF-TOKENヘッダーに渡される必要があります。AxiosやAngular HttpClientのような一部のHTTPクライアントライブラリは、自動的にこれを行います。JavaScript HTTPライブラリが値を設定しない場合は、このルートによって設定されたXSRF-TOKENクッキーの値と一致するようにX-XSRF-TOKENヘッダーを手動で設定する必要があります。

ログイン

CSRF保護が初期化されたら、Laravelアプリケーションの/loginルートにPOSTリクエストを行う必要があります。この/loginルートは、手動で実装するか、Laravel Fortifyのようなヘッドレス認証パッケージを使用して実装できます。

ログインリクエストが成功すると、認証され、アプリケーションのルートへのその後のリクエストは、Laravelアプリケーションがクライアントに発行したセッションクッキーを介して自動的に認証されます。さらに、アプリケーションが/sanctum/csrf-cookieルートにリクエストを行ったため、その後のリクエストは、JavaScript HTTPクライアントがXSRF-TOKENクッキーの値をX-XSRF-TOKENヘッダーに送信する限り、自動的にCSRF保護を受けるはずです。

もちろん、ユーザーのセッションがアクティビティの欠如により期限切れになると、Laravelアプリケーションへのその後のリクエストは401または419のHTTPエラーレスポンスを受け取る可能性があります。この場合、ユーザーをSPAのログインページにリダイレクトする必要があります。

独自の/loginエンドポイントを作成することは自由ですが、標準のセッションベースの認証サービスを使用してユーザーを認証することを確認する必要があります。通常、これはweb認証ガードを使用することを意味します。

ルートの保護

すべての受信リクエストが認証されるようにルートを保護するには、APIルート内のroutes/api.phpファイルにsanctum認証ガードを添付する必要があります。このガードは、受信リクエストがSPAからの状態を持つ認証されたリクエストとして認証されるか、サードパーティからのリクエストの場合は有効なAPIトークンヘッダーを含むことを保証します:

  1. use Illuminate\Http\Request;
  2. Route::get('/user', function (Request $request) {
  3. return $request->user();
  4. })->middleware('auth:sanctum');

プライベートブロードキャストチャンネルの認可

SPAがプライベート/プレゼンスブロードキャストチャンネルで認証する必要がある場合、アプリケーションのbootstrap/app.phpファイルに含まれるwithRoutingメソッドからchannelsエントリを削除する必要があります。代わりに、withBroadcastingメソッドを呼び出して、アプリケーションのブロードキャストルートに正しいミドルウェアを指定できるようにします:

  1. return Application::configure(basePath: dirname(__DIR__))
  2. ->withRouting(
  3. web: __DIR__.'/../routes/web.php',
  4. // ...
  5. )
  6. ->withBroadcasting(
  7. __DIR__.'/../routes/channels.php',
  8. ['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']],
  9. )

次に、Pusherの認可リクエストが成功するためには、Laravel Echoを初期化する際にカスタムPusher authorizerを提供する必要があります。これにより、アプリケーションがクロスドメインリクエスト用に適切に構成されたaxiosインスタンスを使用するようにPusherを設定できます:

  1. window.Echo = new Echo({
  2. broadcaster: "pusher",
  3. cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
  4. encrypted: true,
  5. key: import.meta.env.VITE_PUSHER_APP_KEY,
  6. authorizer: (channel, options) => {
  7. return {
  8. authorize: (socketId, callback) => {
  9. axios.post('/api/broadcasting/auth', {
  10. socket_id: socketId,
  11. channel_name: channel.name
  12. })
  13. .then(response => {
  14. callback(false, response.data);
  15. })
  16. .catch(error => {
  17. callback(true, error);
  18. });
  19. }
  20. };
  21. },
  22. })

モバイルアプリケーション認証

Sanctumトークンを使用して、モバイルアプリケーションのAPIへのリクエストを認証することもできます。モバイルアプリケーションリクエストの認証プロセスは、サードパーティAPIリクエストの認証に似ていますが、APIトークンを発行する方法には小さな違いがあります。

APIトークンの発行

始めるには、ユーザーのメールアドレス/ユーザー名、パスワード、デバイス名を受け入れるルートを作成し、それらの資格情報を新しいSanctumトークンと交換します。このエンドポイントに与えられた「デバイス名」は情報提供の目的であり、任意の値を指定できます。一般的に、デバイス名の値は、ユーザーが認識できる名前、たとえば「NunoのiPhone 12」であるべきです。

通常、モバイルアプリケーションの「ログイン」画面からトークンエンドポイントにリクエストを行います。エンドポイントはプレーンテキストのAPIトークンを返し、それをモバイルデバイスに保存して追加のAPIリクエストに使用できます:

  1. use App\Models\User;
  2. use Illuminate\Http\Request;
  3. use Illuminate\Support\Facades\Hash;
  4. use Illuminate\Validation\ValidationException;
  5. Route::post('/sanctum/token', function (Request $request) {
  6. $request->validate([
  7. 'email' => 'required|email',
  8. 'password' => 'required',
  9. 'device_name' => 'required',
  10. ]);
  11. $user = User::where('email', $request->email)->first();
  12. if (! $user || ! Hash::check($request->password, $user->password)) {
  13. throw ValidationException::withMessages([
  14. 'email' => ['The provided credentials are incorrect.'],
  15. ]);
  16. }
  17. return $user->createToken($request->device_name)->plainTextToken;
  18. });

モバイルアプリケーションがトークンを使用してアプリケーションにAPIリクエストを行う場合、AuthorizationヘッダーにBearerトークンとしてトークンを渡す必要があります。

モバイルアプリケーションのトークンを発行する際には、トークンの能力を指定することも自由です。

ルートの保護

前述のように、すべての受信リクエストが認証されるようにルートを保護するには、sanctum認証ガードをルートに添付する必要があります:

  1. Route::get('/user', function (Request $request) {
  2. return $request->user();
  3. })->middleware('auth:sanctum');

トークンの取り消し

モバイルデバイスに発行されたAPIトークンをユーザーが取り消せるようにするには、名前とともに「取り消し」ボタンを「アカウント設定」部分のUIにリストすることができます。ユーザーが「取り消し」ボタンをクリックすると、データベースからトークンを削除できます。ユーザーのAPIトークンには、tokensリレーションシップを介してアクセスできます。これはLaravel\Sanctum\HasApiTokensトレイトによって提供されます:

  1. // Revoke all tokens...
  2. $user->tokens()->delete();
  3. // Revoke a specific token...
  4. $user->tokens()->where('id', $tokenId)->delete();

テスト

テスト中に、Sanctum::actingAsメソッドを使用してユーザーを認証し、トークンに付与されるべき能力を指定できます:

  1. use App\Models\User;
  2. use Laravel\Sanctum\Sanctum;
  3. test('task list can be retrieved', function () {
  4. Sanctum::actingAs(
  5. User::factory()->create(),
  6. ['view-tasks']
  7. );
  8. $response = $this->get('/api/task');
  9. $response->assertOk();
  10. });
  1. use App\Models\User;
  2. use Laravel\Sanctum\Sanctum;
  3. public function test_task_list_can_be_retrieved(): void
  4. {
  5. Sanctum::actingAs(
  6. User::factory()->create(),
  7. ['view-tasks']
  8. );
  9. $response = $this->get('/api/task');
  10. $response->assertOk();
  11. }

すべての能力をトークンに付与したい場合は、*actingAsメソッドに提供される能力リストに含める必要があります:

  1. Sanctum::actingAs(
  2. User::factory()->create(),
  3. ['*']
  4. );