Laravel8でSanctumを使いAPI認証を実装する
やること
LaravelのSanctumを使用し、API認証を実装します。
/meは認証されているユーザーしかアクセスできないよう実装します。
Sanctumのインストールとセットアップ
Sanctumのインストール
composer require laravel/sanctum
次にSanctum設定ファイルと移行ファイルを公開します。
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
データベースのマイグレーションを実行します。
これでAPIトークンを格納するためのテーブルを作成してくれます。
php artisan migrate
デフォルトでLaravelに付属するテーブルに加え、すべてのトークンを格納するpersonal_access_tokensテーブルがデータベースに作成されます。
mysql> show tables; +------------------------+ | Tables_in_project01 | +------------------------+ | failed_jobs | | migrations | | password_resets | | personal_access_tokens | | users | +------------------------+ 5 rows in set (0.00 sec)
APIトークンの発行
Sanctumを使用すると、アプリケーションへのAPIリクエストの認証に使用できるAPIトークン/パーソナルアクセストークンを発行できます。 APIトークンを使用してリクエストを行う場合、トークンは「Bearer」トークンとして「Authorization」ヘッダに含める必要があります。
use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; }
APIの作成
Controllerの作成
php artisan make:controller AuthController
ユーザー登録
ユーザー登録を行うルートを作成します。
routes/api.php
use App\Http\Controllers\AuthController; // API認証 Route::post('/register', [AuthController::class, 'register']);
ユーザー登録メソッドを作成します。
app/Http/Controllers/AuthController.php
use Illuminate\Support\Facades\Hash; class AuthController extends Controller { public function register(Request $request) { $validatedData = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:8', ]); $user = User::create([ 'name' => $validatedData['name'], 'email' => $validatedData['email'], 'password' => Hash::make($validatedData['password']), ]); return response()->json('User registration completed'); } }
リクエストにバリデーションをかけ、問題なければUserテーブルに格納し、"User registration completed" を返します。
ログイン機能
ログインルートを作成します。
routes/api.php
Route::post('/login', [AuthController::class, 'login']);
AuthControllerの中にlogin()メソッドを追加します。
public function login(Request $request) { if (!Auth::attempt($request->only('email', 'password'))) { return response()->json([ 'message' => 'User Not Found' ], 401); } $user = User::where('email', $request['email'])->firstOrFail(); $user->tokens()->delete(); $token = $user->createToken('auth_token_userID_'.$user->id)->plainTextToken; return response()->json([ 'access_token' => $token, 'token_type' => 'Bearer', ]); }
POSTできたメールとパスワードが違えば、401 User Not Found を返します。
1ユーザーに対して複数のtokenができてしまうので、delete()で古いtokenは削除します。
createToken()メソッドを使いトークン名を"auth_token_userID_xx" とし、作成されたユーザーに新しいアクセストークンを生成します。
plainTextTokenプロパティを使用してトークンのプレーンテキスト値にアクセスすることができます。
最後に作成されたトークンとトークンタイプを含むJSONレスポンスを返します。
認証済みユーザのみアクセスできる画面の作成
認証済みユーザーの場合、ユーザー情報を返す画面(/me)を作成します。
routes/api.php
Route::get('/me', [AuthController::class, 'me'])->middleware('auth:sanctum');
sanctumガードを使用することでエンドポイントのリクエストのヘッダーに有効なAPIトークンが含まれるようになります。
AuthControllerには以下のメソッドを追加します。
public function me(Request $request) { return $request->user(); }
またAPIをテストする前に、app/Providers/RouteServiceProvider.phpの以下のコメントを解除してください。
protected $namespace = 'App\\Http\\Controllers';
APIをテストする
Laravel+Homesteadで環境構築しており、/etc/hosts でipアドレスを以下のように設定しているため
192.168.30.10 project01.test
APIにはhttp://project01.test/apiからアクセスします。
そこまで構築するのがめんどくさい場合は php artisan serve
コマンドでサーバーを起動させ "http://127.0.0.1:8000/api" でアクセスするとよいと思います。
今回はVSCodeの拡張機能のThunder Client を使用します。
marketplace.visualstudio.com
VSCode にインストール後左に表示されるアイコン -> New Requestをクリックしたら始めることができます。
ユーザー登録をする
[POST] http://project01.test/api/register?name=yamada&email=xxx@mail.com&password=pAssw0rd
http://project01.test/api/register宛にname, email, passwordを含むPOSTリクエストを作成します。パスワードは8文字以上必要です。
ログインする
emailとpasswordを入力し、http://project01.test/api/loginにPOSTリクエストを行います。
[POST] http://project01.test/api/login?email=xxx@mail.com&password=pAssw0rd
仮に間違ったemailやpasswordをパラメーターで渡すと、"message": "User Not Found"
と返ってきます。
/meにアクセスする
ここで返ってきたaccess_tokenをAuth のBearerに入力します。
[GET] http://project01.test/api/me
有効なトークンなため認証されたユーザー情報が返ってきました。
無効なトークンを入力すると500エラーが返ってきます。
参考サイト
Laravel SanctumでPHPのRESTful APIを構築する
おまけ
createToken()で第一引数に名前をつける必要性ってなんだろうと思いました。 軽く調べたところ
ウェブ用とモバイルアプリ用の2種類のトークンがあり、それぞれの種類の有効期限が異なる場合は、「ウェブトークン」や「モバイル」などの特定の名前で各種類をグループ化できます。
だそうです。