メインコンテンツまでスキップ

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アプリケーションと同じリポジトリに存在するか、Vue CLIを使用して作成されたSPAやNext.jsアプリケーションなど、完全に別のリポジトリに存在するかもしれません。

この機能では、Sanctumはどのようなトークンも使用しません。代わりに、SanctumはLaravelの組み込みのクッキーベースのセッション認証サービスを使用します。通常、Sanctumはこれを達成するためにLaravelの web 認証ガードを利用します。これにより、CSRF保護、セッション認証、およびXSSを介した認証資格情報の漏洩から保護されます。

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

注記

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

インストール

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

php artisan install:api

次に、SPAを認証するためにSanctumを利用する予定がある場合は、このドキュメントの 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トークンとして含める必要があります。

ユーザーにトークンを発行するためには、Userモデルが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トレイトによって提供されるtokensEloquentリレーションシップを使用して、ユーザーのすべてのトークンにアクセスできます:

    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によるリクエストの開始

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

ただし、これは必ずしもアプリケーションがユーザーにアクションを実行させる必要があることを意味するわけではありません。通常、アプリケーションの認可ポリシーが、トークンが権限を与えられたかどうかやユーザーインスタンス自体がアクションを実行できるかどうかをチェックします。

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

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

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

ルートの保護

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

なぜ sanctum ガードを使用してアプリケーションの routes/web.php ファイル内でルートを認証することをお勧めするのか疑問に思うかもしれません。覚えておいてください、Sanctum は最初に Laravel の通常のセッション認証クッキーを使用して着信リクエストを認証しようとします。そのクッキーが存在しない場合、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 メソッドの第三引数として有効期限を指定することができます。

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 の組み込みのクッキーベースのセッション認証サービスを使用します。この認証アプローチは、CSRF 保護、セッション認証の利点を提供し、XSS を介した認証資格情報の漏洩に対しても保護します。

警告

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

設定

最初のパーティドメインの構成

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

警告

ポートを含む URL 経由でアプリケーションにアクセスしている場合(127.0.0.1:8000)、ドメインにポート番号を含める必要があります。

Sanctum ミドルウェア

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

    ->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
})

CORS とクッキー

SPA から別のサブドメインで実行される SPA からアプリケーションに認証できない場合、CORS(Cross-Origin Resource Sharing)またはセッションクッキーの設定が誤っている可能性があります。

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

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 クライアントで同等の構成を行う必要があります:

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

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

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

認証

CSRF 保護

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

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

このリクエスト中に、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 エンドポイントを独自に作成することは自由ですが、Laravelが提供する標準のセッションベースの認証サービスを使用してユーザーを認証することを確認する必要があります。通常、これは web 認証ガードを使用することを意味します。

SPAルートの保護

すべての着信リクエストが認証されるようにルートを保護するには、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を提供する必要があります。これにより、Pusherをaxiosインスタンスを使用するように構成できるようになります。これは、クロスドメインリクエストに適切に構成された axiosインスタンスを使用することを可能にします。

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's 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の「アカウント設定」部分に、名前と共に「取り消し」ボタンを表示することで、ユーザーがそれらを一覧表示できるようにすることができます。ユーザーが「取り消し」ボタンをクリックすると、トークンはデータベースから削除されます。ユーザーのAPIトークンには、Laravel\Sanctum\HasApiTokensトレイトによって提供されるtokensリレーションシップを介してアクセスできることを覚えておいてください。

    // 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(),
['*']
);