Laravelで簡易的なAPI認証を実装する

## はじめに

今回は、認証機能を簡単に理解できるよう、簡易的なAPI認証をLaravelで実装していきます。認証に関する知識が不足していると感じたため、シンプルなAPIを作成することから始めました。

## 実装のゴール

* APIを呼び出してトークンを発行する（ログイン機能）
* トークンを使って認証が必要なページにアクセスする（例: マイページ）
* トークンを削除する（ログアウト機能）

## 開発環境

PHP: 8.3.8
Laravel: 11.11.1
composer: 2.7.6
OS: Ubuntu 22.04.4 LTS

## UserモデルにHasApiTokensを追加

まず、 `User` モデルに `HasApiTokens` トレイトを追加し、ユーザー認証をトークンで行えるようにします。

```php:app/Models/User.php
<?php

namespace App\Models; 

～省略～

use Laravel\Sanctum\HasApiTokens; // 追加

class User extends Authenticatable
{

    use HasApiTokens, HasFactory, Notifiable; // 追加

    ～省略～

}
```

## `guards`設定を追加

認証に必要な `guards` を `config/auth.php` に追加します。

```php:config/auth.php
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        // API用のguardを追加
        'api' => [
            'driver' => 'sanctum',
            'provider' => 'users',
        ],
    ],
```

## ミドルウェアの設定

認証機能に必要なミドルウェアを `bootstrap/app.php` に追加します。

```php:bootstrap/app.php
    ->withMiddleware(function (Middleware $middleware) {
        // ミドルウェアを追加
        $middleware->statefulApi();
    })
```

## 認証用コントローラーの作成

次に、認証用のコントローラー AuthController を作成します。

```bash:実行コマンド
php artisan make:controller AuthController
```

`AuthController` の実装は以下の通りです。

```php:app/Http/Controllers/AuthController.php
<?php

namespace App\Http\Controllers; 

use Illuminate\Http\Request; 
use Illuminate\Support\Facades\Auth; 

class AuthController extends Controller
{

    // ログイン処理
    public function login(Request $request)
    {
        $credentials = [
            'email' => $request->email,
            'password' => $request->password
        ];

        if (Auth::attempt($credentials)) {
            $user = $request->user();
            $token = $user->createToken('AccessToken')->plainTextToken;

            return response()->json(['token' => $token], 200);
        } else {
            return response()->json(['error' => '認証に失敗しました。'], 401);
        }
    }

    // 認証ユーザー情報の取得
    public function user(Request $request)
    {
        return response()->json([
            'name' => $request->user()->name,
            'email' => $request->user()->email,
        ]);
    }

    // ログアウト処理
    public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();
        return response()->json(['message' => 'ログアウトしました。'], 200);
    }
}
```

上記のように、ログイン、ユーザー情報取得、ログアウトの機能をコントローラーに実装します。

## 認証ルートの設定

APIのルートは `routes/api.php` に定義します。

```php:routes/api.php
Route::post('/login', [App\Http\Controllers\AuthController::class, 'login'])->name('login');

Route::middleware(['auth:sanctum'])->group(function () {
    Route::get('/user', [App\Http\Controllers\AuthController::class, 'user']);
    Route::post('/logout', [App\Http\Controllers\AuthController::class, 'logout'])->name('logout');
});
```

## Userテーブルにデータを追加

`User` テーブルに認証用のデータを挿入するため、Seederを作成します。

```bash: 実行コマンド
php artisan make:seeder UserSeeder
```

`UserSeeder` の内容は以下の通りです。

```php:database/seeders/UserSeeder.php
<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        User::create([
            'name' => 'testuser',
            'email' => 'test@examle.com',
            'password' => Hash::make('password'),
        ]);
    }
}
```

Seederを実行して、 `User` テーブルにデータを挿入します。

```bash: 実行コマンド
php artisan db:seed
```

## Postmanを使って確認

ここまでの設定が完了したら、Postmanなどのツールを使ってAPIが正しく動作するか確認しましょう。具体的には、ログイン、マイページへのアクセス、ログアウトが正常に機能しているかをチェックします。

まずはサーバーを起動します。

```bash:実行コマンド
php artisan serve
```

これでサーバーが立ち上がりました。

次に、Postmanで `http://127.0.0.1:8000/api/login` に対してPOSTリクエストを送信します。

```json:リクエスト
{
  "email":"test@example.com", 
  "password":"password"
}
```

送信すると、以下のようなJSONが返ってきます。

```json:レスポンス
{
    "token": "6|aloZxLg2jakSShHcu5jQiCxQaDDOB1ajRcMIWvL5e3b558e3"
}
```

ここで取得した `token` を、Postmanの「Bearerトークン」に設定し、次に `http://127.0.0.1:8000/api/user` にGETリクエストを送ります。

```markdown:リクエスト
リクエスト: なし
```

送信すると、以下のようなJSONが返ってきます。

```json:レスポンス
{
    "name": "Test User",
    "email": "test@example.com"
}
```

続いて、 `http://127.0.0.1:8000/api/logout` にPOSTリクエストを送信しましょう。

```markdown:リクエスト
リクエスト: なし
```

送信すると、以下のようなJSONが返ってきます。

```json:レスポンス
{
    "message": "ログアウトしました。"
}
```

ログアウトが成功しました。ログアウト後は、 `http://127.0.0.1:8000/api/user` や `http://127.0.0.1:8000/api/logout` にアクセスできないことを確認してください。また、不正なデータを使って `http://127.0.0.1:8000/api/login` にリクエストを送った際、エラーメッセージが返ってくることも確認しておきましょう。

## おわりに

今回の実装を通して、認証機能について基本的な理解を深めることができました。このコードは本番環境で使うには改善の余地がありますが、今後もさらに調査・学習を続け、よりロバストな認証システムを作成できるようにしていきたいと思います。

## 参考記事

https://readouble.com/laravel/11.x/ja/authentication.html

https://readouble.com/laravel/11.x/ja/sanctum.html#spa-authentication

https://qiita.com/104dev/items/0787a81f7dda892ce86a

https://www.divx.co.jp/media/techblog-221114