API認証に使えるLaravel SanctumのToken APIを完全理解
Laravel Sanctumはシングルページアプリケーション(SPA)に対する認証機能を提供しているLaravelのパッケージです。 Tokenを利用したAPI TokensとCookieを利用したSPA Authenticationの2つの機能がありますが、本文書ではAPIトークンの利用方法について説明を行っています。
APIトークンについてはシンプルな仕組みで構築したLaravelアプリケーション上にアカウントを持ったユーザがログインを行いアカウント毎にトークンを発行します。発行したトークンをLaravelにアクセスしたいアプリケーション側でリエクストのHeader(ヘッダー)に組み込んで送信することでLaravelで認証が行われアクセス可否が決まります。
クラウドサービスに対してAPI経由で情報を取得したい場合、アカウントを作成後にアクセスキーを取得するという経験をしたことがある人も多数いるかと思います。まさにあのアクセスキーの取得がLaravel SanctumでのAPIトークンの発行になります。
トークンはただの文字列なので他の人にトークンの文字列がわかると誰でもアクセスすることが可能となるので厳重に管理する必要があります。
Laravel8からはJetsteamによりトークンを管理する機能が実装されているので管理機能を実装することなくすぐに活用することができます。
Laravel8で動作確認を行なっています。最新のLaravel9ではLaravel Sanctumがインストールされているのでインストールなど初期設定を行う必要はありません。
目次
SanctumのToken APIの流れ
SanctumのAPIトークンの実際の流れは以下の通りです。
- LaravelでSanctumのパッケージのインストール、テーブルの作成など初期設定を行う。
- ユーザが自分でトークンを発行する。(テーブルにトークン情報を保存)
- Laravel側でアクセス制限を行いたいルーティングにミドルウェアのauth:sanctumを設定する。
- ユーザがアクセスするとミドルウェアの中でヘッダーの中のトークンを取り出しテーブルに保存されているトークンと中身が一緒であるかチェックを行う。
- 送られてきたTokenがテーブルのトークンと一致すればそのルーティングにアクセスすることができる。もし一致しない場合やトークンがそもそもヘッダーに入っていない場合は401のunauthenticatedが戻される
APIトークンはCookieなどは一切利用していません。
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;
トークンの発行
トークンの発行はcreateTokenメソッドを利用します。createTokenはHasApiTokensトレイトの中に含まれているのでUserモデルからcreateTokenメソッドを実行することができます。createTokenメソッドではcreateメソッドを使って先ほどphp artisan migrateで作成したpsersonal_access_tokensテーブルにトークンを保存していることがわかります。
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);
}
トークンはランダムの40桁の文字列をsha256のハッシュアルゴリズムでハッシュ化していることが確認できます。トークンが一体どのように作成されるのか疑問に思った人もいるかもしれませんがたったこれだけのシンプルなものです。手元のトークンが正しいのかどうか迷った時はトークンは40桁だということを思い出してまずは長さをチェックしてください。
実際にトークンを作成してみましょう。トークンを発行するためにはユーザが必要となります。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)メソッドを使ってtinkerで作成したユーザでLaravelにログインを行いcreateTokenメソッドでトークンを作成します。loginUsingIdの引数の1は先ほど作成したユーザのIDの1です。
Route::get('/', function () {
// return view('welcome');
$user = Auth::loginUsingId(1);
$token = $user->createToken('test');
dd($token);
});
トークンを作成する際に名前をつけていますがアクセスするAPI側でその名前を利用することもなくLaravel上で管理するためのものなので任意の名前をつけることができます。自分が管理する場合に識別できるような名前をつけてください。
ブラウザで確認すると作成したトークンを確認することができます。最初の1|はトークに含まれてないので注意してください。
plainTextTokenがAPIでLaravelにアクセスする際に利用するトークンです。
トークンはデータベースにも保存されるのでデータベース管理ソフトか直接コマンドラインでデータベースにアクセスしてください。作成したトークンを確認することができますがデータベースのpersonal_access_tokensテーブルのtoken列に保存されている文字列と先ほど取得したplainTexgtTokenの値が異なります。これはトークンがデータベースにはハッシュ化されて保存されているためです。
トークンの作成はJetstreamで可能
トークンの作成はログインしてcreateTokenでき、作成したトークンはデータベースに入っており$user->tokensでユーザが作成したトークンの情報を取得することができるので作成・管理は難しいものではありません。
Laravel8から追加されたJetstreamにはトークンを管理する機能が含まれているのでJetstreamを利用すれば管理機能を実装する必要もありません。Jetstreamを参考に管理する機能を実装することも可能です。先ほどTokenの作成方法を確認したので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でユーザの登録を行ってください。
ログイン完了後に右上のメニューでAPI Tokensを選択してください。もしjestream.phpファイルでAPIを有効にしていない場合はこのAPI Tokensは表示されません。
トークンの作成と作成したトークンを確認することができます。先ほど確認した名前をtestにしたトークンも表示されています。
ブラウザ上から作成したい場合は名前(Name)を入力して実行できるPermissionの設定をチェックして”CREATE”ボタンを押してください。
トークンが表示されます。ここで注意が必要なのはメッセージに表示される通り作成後はトークンは二度と表示されません(it won’t be shown again)。トークンを忘れた場合は再作成してください。
コピーしたらCLOSEボタンを押してください。
データベース上でのトークンも確認しておきましょう。permissionにreadを選択してのでabilitiesがreadになっていることが確認できます。
abilities(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となっていましたがそれ以外の名前でも問題がないことがわかります。
alilityの設定はLaravel上ではtokenCanメソッドを使って実行するコードの分岐に利用することができます。
if ($user->tokenCan('server:update')) {
//
}
Tokenを利用したアクセス
トークンの発行方法はここまでの説明で理解できたと思います。最後にトークンを使ってAPIでLaravelにアクセスが可能なのか確認します。
sanctumを利用する場合は認証を行うルーティングに対してmiddlewareでauth:sanctumを設定します。デフォルトではauth:apiと設定されているはずです。web.phpファイルにルーティング/userを追加します。api.phpファイルで行う場合にはアクセスする際のパスに注意してください。/userではなく/api/userになります。
use Illuminate\Http\Request;
//略
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Visual Studio CodeのRest Clientを利用
Tokenを利用してアクセスの動作確認を行う際にVisual Studio Codeをエディターとして利用している場合はREST Clientが便利です。その他の方法であればPostMan、curlコマンドを利用することができますがREST Clientが簡単です。curlコマンドの利用方法もこの後に説明しています。
Visual Studio CodeでREST ClientのExtensionをインストールしてください。
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メソッドで作成したTokenは利用せずに/userにアクセスを行っています。アクセスは行われていますがエラーのステータスコード401でUnauthrorizedが戻されていることが確認できます。TokenがないのでSanctumの認証に引っかかったことがわかります。
次に作成したトークンを設定してアクセスを行ってみましょう。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と一緒にユーザの情報が表示されます。
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();
ユーザが保持するトークン情報の取得
ユーザが持っているトークンは以下のように確認することができます。
foreach ($user->tokens as $token) {
//
}
Laravel Sanctumという名前から想像ができないAPI Tokenを利用した認証ですが、仕組みもシンプルなので簡単に使いこなすことができるかと思います。
コードベースで理解した場合は以下の記事がお勧めです。