コンテンツへスキップ

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ヘッダーを調べます。

lightbulb

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トークン認証

lightbulb

独自のファーストパーティ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.phproutes/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による認証資格情報の漏洩からも保護します。

exclamation

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

設定

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

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

exclamation

ポート(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設定が、値がTrueAccess-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のログインページにリダイレクトする必要があります。

exclamation

独自の/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トークンとして渡す必要があります。

lightbulb

モバイルアプリケーションのトークンを発行する場合は、トークンの機能を指定することもできます。

ルートの保護

既に説明したように、ルートに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(),
['*']
);