Laravel 10.x Laravel Sanctum

イントロダクション

Laravel Sanctum(サンクタム、聖所)は、SPA(シングルページアプリケーション)、モバイルアプリケーション、およびシンプルなトークンベースのAPIへ、軽い認証システムを提供します。Sanctumを使用すればアプリケーションの各ユーザーは、自分のアカウントに対して複数のAPIトークンを生成できます。これらのトークンには、そのトークンが実行できるアクションを指定するアビリティ/スコープが付与されることもあります。

仕組み

LaravelSanctumは、2つの別々の問題を解決するために存在します。ライブラリを深く掘り下げる前に、それぞれについて説明しましょう。

APIトークン

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

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

SPA認証

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

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

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

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

インストール

Note: Laravelの最新バージョンは、あらかじめLaravel Sanctumを含んでいます。ただし、アプリケーションのcomposer.jsonファイルがlaravel/sanctumを含んでいない場合は、以下のインストール手順に従ってください。

Laravel Sanctumは、Composerパッケージマネージャを介してインストールできます。

composer require laravel/sanctum

次に、vendor:publish Artisanコマンドを使用してSanctum設定ファイルと移行ファイルをリソース公開する必要があります。sanctum設定ファイルは、アプリケーションのconfigディレクトリに配置されます。

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

最後に、データベースのマイグレーションを実行する必要があります。Sanctumは、APIトークンを格納するための1つのデータベーステーブルを作成します。

php artisan migrate

次に、Sanctumを使用してSPAを認証する場合は、Sanctumのミドルウェアをアプリケーションのapp/Http/Kernel.phpファイル内のapiミドルウェアグループに追加する必要があります。

'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

マイグレーションのカスタマイズ

Sanctumのデフォルトマイグレーションを使用しない場合は、App\Providers\AppServiceProviderクラスのregisterメソッドでSanctum::ignoreMigrationsメソッドを呼び出す必要があります。次のコマンドを実行して、デフォルトマイグレーションをエクスポートできます。php artisan vendor:publish --tag=sanctum-migrations

設定

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

通常は必須ではありませんが、Sanctumが内部で使用するPersonalAccessTokenモデルを自由に拡張できます。

use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;

class PersonalAccessToken extends SanctumPersonalAccessToken
{
    // ...
}

次に、Sanctumが提供するusePersonalAccessTokenModelメソッドを使用して、カスタムモデルを使用するようにSanctumに指示します。通常、このメソッドは、アプリケーションのサービスプロバイダの1つのbootメソッドで呼び出す必要があります。

use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;

/**
 * 全アプリケーションサービスの初期起動処理
 */
public function boot(): void
{
    Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}

APIトークン認証

Note: 独自のファーストパーティSPAを認証するためにAPIトークンを使用しないでください。代わりに、Sanctumの組み込みのSPA認証機能を使用してください。

APIトークンの発行

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

ユーザーへのトークンの発行を開始するには、ユーザーモデルで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つのミドルウェアを用意しており、受信リクエストが所定の能力を付与されたトークンで認証されていることを確認するために使用できます。最初に、アプリケーションのapp/Http/Kernel.phpファイルの$middlewareAliasesプロパティへ、以下のミドルウェアを追加します。

'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,

受信リクエストのトークンがリストしたすべてのアビリティを持っていることを確認するには、abilitiesミドルウェアをルートへ割り付けます。

Route::get('/orders', function () {
    // トークンは"check-status"と"place-orders"アビリティの両方を持っている
})->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')

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

ルートの保護

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

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

use Illuminate\Http\Request;

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

トークンの削除

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

// 全トークンの削除
$user->tokens()->delete();

// 現在のリクエストの認証に使用されたトークンを取り消す
$request->user()->currentAccessToken()->delete();

// 特定のトークンを取り消す
$user->tokens()->where('id', $tokenId)->delete();

トークンの有効期限

デフォルトでは、Sanctumトークンに有効期限はなく、トークンの取り消しによってのみ無効化される可能性があります。しかし、アプリケーションのAPIトークンに有効期限を設定したい場合は、アプリケーションのsanctum設定ファイルで定義する、expiration設定オプションで指定できます。このオプションは、発行したトークンを期限切れと判断するまでの分数を定義します。

'expiration' => 525600,

アプリケーションのトークン有効期限を設定した場合、有効期限が切れたトークンを削除するタスクをスケジュールもしたくなるでしょう。さいわいSanctumは、sanctum:prune-expired Artisanコマンドが用意されており、これが利用可能です。例として、24時間以上有効期限が切れているトークンデータベースのレコードをすべて削除するようにスケジュールタスクを設定してみましょう。

$schedule->command('sanctum:prune-expired --hours=24')->daily();

SPA認証

Sanctumは、Laravelを利用したAPIと通信する必要があるシングルページアプリケーション(SPA)を認証する簡単な手段を提供するためにも存在しています。これらのSPAは、Laravelアプリケーションと同じリポジトリに存在する場合もあれば、完全に別個のリポジトリである場合もあります。

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

Warning!! 認証を行うには、SPAとAPIが同じトップレベルドメインを共有している必要があります。ただし、異なるサブドメインに配置しても構いません。さらに、必ずリクエストへAccept: application/jsonヘッダと、RefererOriginヘッダのどちらかを付け、送信してください。

設定

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

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

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

Sanctumミドルウェア

次に、Sanctumのミドルウェアをapp/Http/Kernel.phpファイル内のapiミドルウェアグループに追加する必要があります。このミドルウェアは、SPAからの受信リクエストがLaravelのセッションクッキーを使用して認証できるようにすると同時に、サードパーティまたはモバイルアプリケーションからのリクエストがAPIトークンを使用して認証できるようにする役割を果たします。

'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

CORSとクッキー

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

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

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

axios.defaults.withCredentials = true;

最後に、アプリケーションのセッションクッキードメイン設定で、ルートドメインのサブドメインを確実にサポートしてください。これを実現するには、アプリケーションのconfig/session.php設定ファイル内でドメインの先頭に.を付けます。

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

認証

CSRF保護

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

axios.get('/sanctum/csrf-cookie').then(response => {
    // ログイン処理…
});

このリクエスト中に、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か419HTTPエラー応答を受け取る可能性があります。この場合、ユーザーをSPAのログインページにリダイレクトする必要があります。

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

ルートの保護

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

use Illuminate\Http\Request;

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

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

SPAがプライベート/プレゼンスブロードキャストチャネルによる認証の必要がある場合は、routes/api.phpファイル内でBroadcast::routesメソッドを呼び出しす必要があります。

Broadcast::routes(['middleware' => ['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'siPhone12」などのユーザーが認識できる名前である必要があります。

通常、モバイルアプリケーションの「ログイン」画面からトークンエンドポイントにリクエストを送信します。エンドポイントはプレーンテキストの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トークンとして渡す必要があります。

Note: モバイルアプリケーションのトークンを発行するときに、トークンのアビリティを自由に指定することもできます。

ルートの保護

前述の通り、ルートにsanctum認証ガードを指定することにより、すべての受信リクエストが認証済みであるようにルートを保護できます。

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

トークンの削除

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

// 全トークンの削除
$user->tokens()->delete();

// 特定のトークンを取り消す
$user->tokens()->where('id', $tokenId)->delete();

テスト

テスト中に、Sanctum::actingAsメソッドを使用して、ユーザーを認証し、トークンに付与するアビリティを指定できます。

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

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる