Laravel Sanctumではシンプルページアプリケーション(SPA)に対しての認証機能を提供しているLaravelのパッケージです。 Tokenを利用したAPI TokensとCookieを利用したSPA Authenticationの2つの機能がありますが、本文書ではシンプルなAPIトークンの利用方法について説明を行っています。

APIトークンについては本当にシンプルでログインしたユーザが自分でトークンを発行し、発行したトークンをAPIのヘッダーに組み込んでアクセスすることでLaravelにアクセスすることが可能となります。

クラウドサービスに対してAPI経由で情報を取得したい場合にアカウントを作成し、その後アクセスキーを取得するという経験をしたこと人もいるかと思います。まさにあのアクセスキーの取得がトークンの発行になります。

トークンはただの文字列なので他の人にトークンの文字列がわかると誰でもアクセスすることが可能となります。

Laravel8からはJetsteamによりトークンを管理する機能が実装されているので管理機能を実装することなくすぐに活用することができます。

Laravel SanctumはLaravel7ではAirlockという名前でした。

Laravel環境の構築

Laravel環境の構築については詳細な説明は行いませんが下記の手順でSanctumの動作確認を行えるLaravel環境を構築していきます。

laravel newコマンドで新規のLaravelプロジェクトを作成します。任意の名前をつけることができここではlaravel-sanctumという名前にしています。


 % laravel new laravel-sanctum

Laravel Sanctumではインストール時トークンを保存するテーブルを作成するのでデータベースが必須です。トークンはデータベースに保存されることになります。本文書では簡易的に利用することが可能なSQLiteデータベースを利用します。

touchコマンドでSQLiteが利用するデータベースファイルを作成します。


 % touch database/database.sqlite

SQLiteにLaravelからアクセスできるように.envファイルでDB_CONNECTIONをsqliteに変更します。デフォルトではmysqlに設定されています。DB_CONNECTION以外のDB_で始める環境変数をすべて削除します。


DB_CONNECTION=sqlite

php artisanコマンドでテーブルの作成を行います。


 % php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (3.25ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (2.09ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (1.96ms)

テーブルが作成できたらSanctumの動作確認までのLaravel環境の構築は完了です。

Laravel Sanctumパッケージのインストール

compserコマンドを利用してlarave/sanctumパッケージのインストールを行います。


 % composer require laravel/sanctum
Using version ^2.9 for laravel/sanctum

php artisan vendor:publishコマンドを使ってsanctumで利用する設定ファイルとsanctum用のテーブルを作成するマイグレーションファイルを作成します。


 % php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Copied Directory [/vendor/laravel/sanctum/database/migrations] To [/database/migrations]
Copied File [/vendor/laravel/sanctum/config/sanctum.php] To [/config/sanctum.php]
Publishing complete.

/database/migrationsフォルダにマイグレーションファイルが作成され、configフォルダの下にsanctum.phpファイルが作成されます。

sanctum用のテーブルを作成するためにphp artisan migrateコマンドを実行します。作成されるテーブル名はpersonal_access_tokensです。テーブル名からもアクセストークンを保存することがわかります。


 % php artisan migrate
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (5.01ms)

APIトークン認証の設定

トークンを利用してLaravelアプリケーションにアクセスするためにはトークンをLaravel側で発行する必要があります。

ユーザがトークンを発行するためにUserモデルにHasApiTokensトレイトを追加する必要があります。


use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
Laravelでは追加でパッケージをインストールした際にUserモデルにトレイトを追加することが多々あります。トレイトの中にはインストールしたパッケージに関するモデルへの追加メソッドが含まれているためです。トレイトを追加する際はトレイトのファイルを開いてどのようなメソッドがあるか確認するとそのパッケージがどのような機能を持っているのか理解することができます。

トークンの発行

トークンの発行(英語ではissue a Token)はcreateTokenメソッドを利用します。createTokenはHasApiTokensトレイトの中に含まれています。


public function createToken(string $name, array $abilities = ['*'])
{
    $token = $this->tokens()->create([
        'name' => $name,
        'token' => hash('sha256', $plainTextToken = Str::random(40)),
        'abilities' => $abilities,
    ]);

    return new NewAccessToken($token, $token->id.'|'.$plainTextToken);
}
英語ではトークンを作成/発行することをissue a Token,トークンを削除することをrevoke a Tokenといいます。本書では意識することなく作成と発行という言葉をつかっていますが同じ意味で使っています。

トークンはランダムの40桁の文字列をsha256のハッシュアルゴリズムでハッシュ化していることが確認できます。トークンが一体どのように作成されるのか疑問に思った人もいるかもしれませんがたったこれだけのシンプルなものです。

実際にトークンを作成してみましょう。トークンを発行するためにはユーザが必要となります。tinkerを利用してユーザを作成してトークンの発行を確認してみましょう。

tinkerでユーザを作成し、idが1であることを確認します。


% php artisan tinker
Psy Shell v0.10.6 (PHP 7.4.10 — cli) by Justin Hileman
>>> $user = new App\Models\User;
=> App\Models\User {#3341}
>>> $user->name = 'John Doe';
=> "John Doe"
>>> $user->email = 'john@example.com';
=> "john@example.com"
>>> $user->password = bcrypt('password');
=> "$2y$10$m6bOlfPYxvradWHyMqcVX.i0WMIgYgKG9y.IAbrN6ogu0f9EpbIkO"
>>> $user->save();
=> true
>>> $user;
=> App\Models\User {#3341
     name: "John Doe",
     email: "john@example.com",
     updated_at: "2021-02-19 14:06:39",
     created_at: "2021-02-19 14:06:39",
     id: 1,
   }

ブラウザでアクセスを行うためphp aritsan serveでLaravelの開発サーバを起動しておきます。


 % php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Sat Feb 20 08:35:38 2021] PHP 7.4.10 Development Server (http://127.0.0.1:8000) started

トークンがどのようなものか確認するためにAuth::loginUsingId(1)メソッドで作成したユーザでLaravelにログインを行いcreateTokenメソッドでトークンを作成します。loginUsingIdの引数の1は先ほど作成したユーザのIDの1です。

ルート(“/”)にアクセスしてID1のユーザでログインしてcreateTokenでトークンを作成しています。


Route::get('/', function () {
    // return view('welcome');
    $user = Auth::loginUsingId(1);
    
    $token = $user->createToken('test');

    dd($token);
});

トークンを作成する際に名前をつけていますがアクセスするAPI側でその名前を利用することもなくLaravel上で管理するためのものなので任意の名前をつけることができます。自分が識別できるような名前をつけてください。

ブラウザで確認すると作成したトークンを確認することができます。

作成されたトークンを確認
作成されたトークンを確認

plainTextTokenがAPIでLaravelにアクセスする際に利用するトークンです。

トークンはデータベースにも保存されるのでデータベース管理ソフトのTablePlusを利用してアクセスします。データベースのtoken列に保存されている文字列と先ほど取得したplainTexgtTokenの値が異なりますがデータベースにはハッシュ化されて保存されているためです。

テーブルに保存されたトークンを確認
テーブルに保存されたトークンを確認

トークンの作成はJetstreamで可能

トークンの作成はログインしてcreateTokenでき、作成したトークンはデータベースに入っており$user->tokensでユーザが作成したトークンの情報を取得することができるので作成・管理は難しいものではありません。

Laravel8から追加sされたJetstreamにはトークンを管理する機能が含まれているのでJetstreamを利用すれば管理機能を実装する必要もありません。Jetstreamを参考に管理する機能を実装することも可能です。

本環境にJetStreamをインストールしてどのようにトークンを作成、管理できるか確認していきましょう。

JetStreamのインストールと設定

composerコマンドでlaravel/jetstreamのパッケージをインストールします。


  % composer require laravel/jetstream
Using version ^2.2 for laravel/jetstream

JetStreamではInertiaとLivewireを選択することができますが今回はInertiaを選択します。


% php artisan jetstream:install inertia
Migration created successfully!

新たなマイグレーションファイルが作成されるのでphp artisan migrateを実行します。


 % php artisan migrate
Migrating: 2014_10_12_200000_add_two_factor_columns_to_users_table
Migrated:  2014_10_12_200000_add_two_factor_columns_to_users_table (3.70ms)
Migrating: 2021_02_19_234647_create_sessions_table
Migrated:  2021_02_19_234647_create_sessions_table (2.77ms)

実行後npmコマンドでJavaScriptライブラリのインストールとビルドを行います。


 % npm install && npm run dev

トークン管理機能の有効化

JetStreamを使うための準備は完了したので最後にJetStreamの設定ファイルであるconfigのjetstream.phpファイルのfeaturesの設定を行います。トークンを利用するためにはFeatures::api()の行にあるコメントを外し有効化します。これでトークンの管理をブラウザ上で行うことができます。


'features' => [
    // Features::termsAndPrivacyPolicy(),
    // Features::profilePhotos(),
    Features::api(),
    // Features::teams(['invitations' => true]),
    Features::accountDeletion(),
],

ブラウザ上からのトークンの作成

JetSteamの設定が完了すると右側にログインのリンクが表示されます。

ユーザ登録、ログインのリンク
ユーザ登録、ログインのリンク

ログインはtinkerで作成したユーザでログインするか新たにRegisterでユーザの登録を行ってください。

Laravel8ログイン画面
Laravel8ログイン画面

ログイン完了後に右上のメニューでAPI Tokensを選択してください。もしjestream.phpファイルでAPIを有効にしていない場合はこのAPI Tokensは表示されません。

メニューからAPI Tokenをクリック
メニューからAPI Tokenをクリック

トークンの作成と作成したトークンを確認することができます。先ほど確認した名前をtestにしたトークンも表示されています。

API Tokens画面
API Tokens画面

ブラウザ上から作成したい場合は名前(Name)を入力して実行できるPermissionの設定をチェックして”CREATE”ボタンを押してください。

トークンを作成
トークンを作成

トークンが表示されます。ここで注意が必要なのはメッセージに表示される通り作成後はトークンは二度と表示されません(it won’t be shown again)。トークンを忘れた場合は再作成してください。

作成したトークンが表示される
作成したトークンが表示される

コピーしたらCLOSEボタンを押してください。

追加されたトークンを確認
追加されたトークンを確認

データベース上でのトークンも確認しておきましょう。permissionにreadを選択してのでabilitiesがreadになっていることが確認できます。

TablePlusで追加したトークンを確認
TablePlusで追加したトークンを確認

abilityies(permission)について

createTokenメソッドでability(permission)を付与したい場合はcreateToken(‘token-name’, [‘server:update’])のように第一引数にトークンの名前を設定し、第2引数に配列で複数のabilityを設定することができます。

実際に複数のalilityをcreateTokenで指定してみましょう。


$token = $user->createToken('test3',['read','server:update']);

作成するとデータベース上のalibity列には下記のように表示されます。JetSteam上ではcreate, update, delete, readとなっていましたがそれ以外の名前でも問題がないことがわかります。

複数のablitityが表示される
複数のablitityが表示される

実際にalilityの設定はLaravel上ではtokenCanメソッドを使って実行するコードの分岐に利用することができます。


if ($user-<tokenCan('server:update')) {
    //
}

Tokenを利用したアクセス

トークンの発行方法はここまでの説明で理解できたと思います。最後にトークンを使ってAPIでLaravelにアクセスが可能なのか確認します。

sanctumを利用する場合は認証を行うルーティングに対してmiddlewareでauth:sanctumを設定します。web.phpファイルにルーティング/userを追加します。


use Illuminate\Http\Request;
//略
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});
動作確認のためweb.phpファイルでRequestクラスを利用しているのでuse Illuminate\Http\Requestを追加することを忘れないのでください。追加しない場合はエラーになります。

Visual Studio CodeのRest Clientを利用

Tokenを利用してアクセスの動作確認を行う際にVisual Studio Codeをエディターに利用している場合はREST Clientが便利です。

REST ClientのExtensionをインストールしてください。

REST clientのインストール
REST clientのインストール

REST Clientを利用するためにはまずXXXX.httpファイルを作成してください。XXXXは任意の名前で拡張子はhttpです。ここではtest.httpというファイルを作成します。

TEST.HTTPに以下を記述するとGETの上にSend Requestが表示されるのでSend Requestをクリックしてください。


GET http://127.0.0.1:8000/user/
Content-Type: application/json
Accept: application/json

GETメソッドで/userにアクセスを行っています。作成したトークンは利用していません。アクセスは行われていますがエラーコード401でUnauthrorizedが戻されていることが確認できます。TokenがないのでSanctumの認証に引っかかったことがわかります。

REST ClientでLaravelにアクセス
REST ClientでLaravelにアクセス

次に作成したトークンを設定してアクセスを行ってみましょう。HeaderにAuthorizationを追加して作成したトークンを指定します。


GET http://127.0.0.1:8000/user/
Content-Type: application/json
Accept: application/json
Authorization: Bearer cdW2iSIAm5OfdQ4mBUFh9XZvrJykN1CCIpmTah9E

Send Requestを実行すると今回は401 Unauthorizedeではなくステータス200と一緒にユーザの情報が表示されます。

Tokenを使ってユーザ情報を取得
Tokenを使ってユーザ情報を取得

curlコマンドを利用

Visual Studio Codeを利用していない場合にはcurlコマンドを利用することもできます。

-Hオプションでheaderを設定します。


% curl -H "Authorization: Bearer cdW2iSIAm5OfdQ4mBUFh9XZvrJykN1CCIpmTah9E" http://127.0.0.1:8000/user/

実行するとユーザ情報が戻されます。


{"id":1,"name":"John Doe","email":"john@example.com","email_verified_at":null,"created_at":"2021-02-19T14:06:39.000000Z","updated_at":"2021-02-19T14:06:39.000000Z","profile_photo_url":"https:\/\/ui-avatars.com\/api\/?name=John+Doe&color=7F9CF5&background=EBF4FF"}

Tokenの削除

JetStreamを利用している場合はブラウザ上でTokenの削除を行うことができます。

コード上で削除する場合は下記のように行うことができます。


//ユーザが作成したトークンを一括削除
$user->tokens()->delete();

//currentAccessTokenで現在利用しているトークンが取得されるのでそのトークンを削除
$request->user()->currentAccessToken()->delete();

//トークンのidがわかっている場合はidを使ってトークンを取得し削除。
$user->tokens()->where('id', $tokenId)->delete();

Laravel Sanctumという名前から想像ができないAPI Tokenを利用した認証ですが、仕組みもシンプルなので簡単に使いこなすことができるかと思います。