APIを作成するにあたり、必ず必要になるのが認証機能です。
Laravelでは標準でトークン認証が用意されていますが、実際のAPIアプリケーションでよく利用されているのはJWT認証です。本記事ではJWT認証を提供するtymon/jwt-auth
パッケージを利用したいと思います。
前回の記事はこちらです。
APIルート
Laravel11にはAPIルートがないため、以下コマンドでAPIルーティングを有効にします。
sail artisan install:api
tymon/jwt-authのインストール
下記コマンドでtymon/jwt-auth
パッケージをインストールして、vendor:publish
コマンドで設定ファイルを作成します。
sail composer require tymon/jwt-auth
sail artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
次に秘密鍵を作成します。
以下コマンド実行後に.envファイルにJWT_SECRET=ランダム文字列が追加されます。
sail artisan jwt:secret
tymon/jwt-authの利用準備
jwt-authを利用するために、Tymon\JWTAuth\Contracts\JWTSubject
インターフェースを実装しておく必要があります。以下の通り、app/User.php
を修正します。
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
/**
* @return mixed
*/
public function getJWTIdentifier(): mixed
{
return $this->getKey();
}
/**
* @return array
*/
public function getJWTCustomClaims(): array
{
return [];
}
}
アプリケーション内で利用するため、config/auth.php
にパッケージ登録されているjwt認証ドライバを追加します。
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
テストデータを作成
この後認証APIを実装するにあたりあらかじめテストデータを用意しておきます。
下記コマンドでSeederを作成します。
Laravel においてSeederとはデータベースにテストデータを一斉に挿入する処理を指します。
sail artisan make:seeder UserSeeder
UserSeeder.php
を下記のように修正します。
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
/**
* @return void
*/
public function run(): void
{
User::factory()->create();
}
}
次にDatabaseSeeder.php
を修正します。
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* @return void
*/
public function run(): void
{
$this->call([
UserSeeder::class
]);
}
}
UserFactory.php
を確認するとランダムにユーザー情報を作成するコードが記述されています。
FactoryとはSeederの一種で同テーブルに一度に大量のデータを投入したいときに利用する仕組みです。
そのため、下記コマンドでデータを挿入します。
sail artisan db:seed
データベースを確認すると下記の通りデータが挿入されたことがわかります。
$ sail mysql
mysql> select * from task_db.users;
+----+------------+-----------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
| id | name | email | email_verified_at | password | remember_token | created_at | updated_at |
+----+------------+-----------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
| 1 | 村山 稔 | fmiyazawa@example.org | 2024-05-17 17:07:19 | $2y$12$u70OUHzPLOKil4AMRa1t0OZRVIwL8E/GEY53cZCvGITwkRmeLAqXO | tMfowrqlu5 | 2024-05-17 17:07:19 | 2024-05-17 17:07:19 |
+----+------------+-----------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+
1 row in set (0.00 sec)
LoginControllerを作成
tymon/jwt-authの利用準備とテストデータの作成が終わったので、認証用のコントローラーを作成します。
とその前にLaravelではAPIを実行する際に必ずURLにapiとprefixをつける決まりになっていますが、本アプリケーションはAPIのみで画面は別途用意する想定のため、prefixを削除します。
prefixを削除するにはbootstrap/app.php
を下記のように修正します。(apiPrefix: '',
を追加します)
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
apiPrefix: '',
)
下記コマンドでLoginController
を作成します。
またroutes/api.php
にルーティングを記述します。
sail artisan make:controller V1/Auth/AuthBaseController --api
sail artisan make:controller V1/Auth/LoginController --api
<?php
namespace App\Http\Controllers\V1\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
class AuthBaseController extends Controller
{
/**
* @param string $token
* @return JsonResponse
*/
protected function respondWithToken(string $token): JsonResponse
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60
]);
}
}
<?php
namespace App\Http\Controllers\V1\Auth;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class LoginController extends AuthBaseController
{
/**
* @param Request $request
* @return JsonResponse
*/
public function store(Request $request): JsonResponse
{
$token = auth()->attempt([
'email' => $request->get('email'),
'password' => $request->get('password')
]);
if (!$token) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
}
routes/api.php
にLoginControllerへのルーティングを追加します。
<?php
use App\Http\Controllers\V1\Auth\LoginController;
use Illuminate\Support\Facades\Route;
Route::prefix('v1')->group(function () {
Route::group([
'middleware' => 'api',
'prefix' => 'auth'
], function () {
Route::post('/login', [LoginController::class, 'store'])->name('login');
});
});
動作確認
実装したコントローラーに以下のコマンドでルクエストを送信してaccess_tokenを作成します。
curl -X POST 'http://localhost/v1/auth/login' \
-H 'content-type: application/json' \
-d '{
"email": "fmiyazawa@example.org",
"password": "password"
}'
リクエスト送信後、以下のレスポンスが返却されます。
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0L3YxL2F1dGgvbG9naW4iLCJpYXQiOjE3MTU5MzUwNzgsImV4cCI6MTcxNTkzODY3OCwibmJmIjoxNzE1OTM1MDc4LCJqdGkiOiJrWmdWV242NlhQaHEyeHhTIiwic3ViIjoiMSIsInBydiI6IjIzYmQ1Yzg5NDlmNjAwYWRiMzllNzAxYzQwMDg3MmRiN2E1OTc2ZjcifQ.RAFSjRX1gWfxkr90YdYd9Ct4cODJYcjZAo1xx8MZk70",
"token_type": "bearer",
"expires_in": 3600
}
コメント