はじめに
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コマンドを介してインストールできます:
php artisan install:api
次に、SPAを認証するためにSanctumを利用する予定がある場合は、このドキュメントのSPA認証セクションを参照してください。
設定
デフォルトモデルのオーバーライド
通常は必要ありませんが、Sanctumが内部で使用するPersonalAccessToken
モデルを拡張することは自由です:
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
class PersonalAccessToken extends SanctumPersonalAccessToken
{
// ...
}
次に、Sanctumが提供するusePersonalAccessTokenModel
メソッドを介してカスタムモデルを使用するように指示できます。通常、このメソッドはアプリケーションの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];
});
ユーザーのすべてのトークンには、tokens
Eloquentリレーションシップを使用してアクセスできます。これはHasApiTokens
トレイトによって提供されます:
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,
]);
})
``````php
Route::get('/orders', function () {
// Token has both "check-status" and "place-orders" abilities...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);
`
``````php
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
を返すことは奇妙に思えるかもしれませんが、APIトークンが常に利用可能であり、tokenCan
メソッドを介して検査できると仮定できることは便利です。このアプローチを取ることで、アプリケーションの認可ポリシー内でtokenCan
メソッドを常に呼び出すことができ、リクエストがアプリケーションのUIからトリガーされたのか、APIのサードパーティ消費者の1人によって開始されたのかを心配する必要がありません。
ルートの保護
すべての受信リクエストが認証されるようにルートを保護するには、sanctum
認証ガードをroutes/web.php
およびroutes/api.php
ルートファイル内の保護されたルートに添付する必要があります。このガードは、受信リクエストが状態を持つ、クッキー認証されたリクエストとして認証されるか、リクエストがサードパーティからのものである場合は有効なAPIトークンヘッダーを含むことを保証します。
なぜアプリケーションのroutes/web.php
ファイル内でsanctum
ガードを使用してルートを認証することを提案するのか疑問に思うかもしれません。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
メソッドに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の組み込みのクッキーに基づくセッション認証サービスを使用します。この認証アプローチは、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
ミドルウェアメソッドを呼び出すことで簡単に実現できます:
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
})
CORSとクッキー
別のサブドメインで実行されるSPAからアプリケーションに認証する際に問題が発生している場合、CORS(クロスオリジンリソースシェアリング)またはセッションクッキー設定が誤って構成されている可能性があります。
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の「ログイン」ページが最初に/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
エンドポイントを作成することは自由ですが、標準のセッションベースの認証サービスを使用してユーザーを認証することを確認する必要があります。通常、これはweb
認証ガードを使用することを意味します。
ルートの保護
すべての受信リクエストが認証されるようにルートを保護するには、APIルート内のroutes/api.php
ファイルに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トークンをユーザーが取り消せるようにするには、名前とともに「取り消し」ボタンを「アカウント設定」部分のUIにリストすることができます。ユーザーが「取り消し」ボタンをクリックすると、データベースからトークンを削除できます。ユーザーのAPIトークンには、tokens
リレーションシップを介してアクセスできます。これはLaravel\Sanctum\HasApiTokens
トレイトによって提供されます:
// 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(),
['*']
);