Hello, World!!

むずかしいことはかけません

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をクリックしたら始めることができます。 f:id:eeko-amaryllis:20220207110633p:plain:w500

ユーザー登録をする

[POST] http://project01.test/api/register?name=yamada&email=xxx@mail.com&password=pAssw0rd  

http://project01.test/api/register宛にname, email, passwordを含むPOSTリクエストを作成します。パスワードは8文字以上必要です。 f:id:eeko-amaryllis:20220207183818p:plain

ログインする

emailとpasswordを入力し、http://project01.test/api/loginにPOSTリクエストを行います。

[POST] http://project01.test/api/login?email=xxx@mail.com&password=pAssw0rd

f:id:eeko-amaryllis:20220207184302p:plain

仮に間違ったemailやpasswordをパラメーターで渡すと、"message": "User Not Found"と返ってきます。 f:id:eeko-amaryllis:20220207183931p:plain

/meにアクセスする

ここで返ってきたaccess_tokenをAuth のBearerに入力します。

[GET] http://project01.test/api/me

f:id:eeko-amaryllis:20220207184425p:plain 有効なトークンなため認証されたユーザー情報が返ってきました。

無効なトークンを入力すると500エラーが返ってきます。

参考サイト

Laravel SanctumでPHPのRESTful APIを構築する

おまけ

createToken()で第一引数に名前をつける必要性ってなんだろうと思いました。 軽く調べたところ

ウェブ用とモバイルアプリ用の2種類のトークンがあり、それぞれの種類の有効期限が異なる場合は、「ウェブトークン」や「モバイル」などの特定の名前で各種類をグループ化できます。

だそうです。

php-createTokenメソッドで文字列を渡す必要があるのはなぜですか? | yaoply.com