Laravel Sanctumの使い方(バックエンド編)

Laravel Laravel

Laravel Sanctumを使って、認証を実装してみました。
バックエンドがLaravelでフロントエンドがNuxt.jsの構成です。

本記事では、Laravel Sanctumの導入から、ユーザー登録APIとログインAPIを作成するところまでを解説しています。
SPAからリクエストを投げて、JSONのレスポンスを返すバックエンドを想定しています。
SPAの実装は別の記事で、記載予定です。

下記のバージョンで検証しました。
laravelのバージョン:8
laravel/sanctumのバージョン:2.8

公式のドキュメントはこちらです。
sanctumのgithubのソースはこちらです。

Laravel Sanctumをインストールする

プロジェクトディレクトリ配下でコマンドを実行して、sanctumの機能のインストールと設定を行います。

composerコマンドでインストールする

下記のcomposerコマンドで導入できます。

$ composer require laravel/sanctum

設定とマイグレーションの作成

導入後に下記のコマンドで設定ファイルとマイグレーションを作成します。

$ php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

コマンドを実行すると、下記のようにコピーした旨のメッセージが表示されます。

$ 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.

コマンドを実行することによって、こちらの設定ファイルと、マイグレーションファイルが用意されます。

/config/sanctum.php
/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php

マイグレーションを実行する

sanctumで追加されたマイグレーションを実行します。
.envでデータベースの設定をしておかないとエラーになるので、設定してから実行しましょう。

$ php artisan migrate

実行すると下記のように、それぞれのマイグレーションがデータベースに適用されます。

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (125.72ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (91.51ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (121.32ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (269.92ms)

データベースを見るとpersonal_access_tokensというsanctumの機能に使うテーブルが作成されていることが確認できます。

Laravel Sanctumを使用する設定

Laravelプロジェクトのソースコードを修正して、sanctumを使用する設定を行います。
app/Http/Kernel.phpapp/Models/User.phpを修正します。

Kernel.phpにミドルウェアの追加

app/Http/Kernel.phpに下記のようにミドルウェアを追加します。
EnsureFrontendRequestsAreStateful::classを追加しました。

'api' => [
  \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
  'throttle:api',
  \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

User ModelにHasApiTokensトレイトを追加する

app/Models/User.phpを開きます。
Userモデルのuse文の箇所にHasApiTokensトレイトを追加して、Apiトークンが発行できるようにします。

<?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 Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
// ・・・省略・・・

ここまでで、Laravel Sanctumの設定は完了です。

APIでユーザー登録を実装する

sanctumとは直接関係ないですが、ログイン時にユーザー登録するAPIの実装と解説を下記に記載しています。

ユーザー登録コントローラーの作成

RegisterController.phpファイルを新規にapp\Http\Controllers\Api配下に作成しました。
下記のartisanコマンドで作成できます。

php artisan make:controller RegisterController

作成後にapp\Http\Controllers\Apiディレクトリを作って、そちらに移動しました。

作成したファイルをこのように修正します。

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\UserCreateRequest;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use \Symfony\Component\HttpFoundation\Response;

class RegisterController extends Controller
{
    public function register(Request $request)
    {
        /** @var Illuminate\Validation\Validator $validator */
        $validator = Validator::make($request->all(), [
            'name' => 'required',
            'email' => 'required|email',
            'password' => 'required'
        ]);

        if ($validator->fails()) {
            return response()->json($validator->messages(), Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        User::create([
            'name' =>  $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        return response()->json('User registration completed', Response::HTTP_OK);
    }
}

解説

RegisterControllerを作って、registerメソッドを実装しました。
ここにAPIのリクエストを飛ばして、登録する予定です。

最初にバリデーションを行って、エラーだった場合は422レスポンスで返すようにしています。
バリデーションは必須とemailだけ付けています。ここには細かく設定する必要があるかと思います。

その後にUserモデルのcreateメソッドを使用して、ユーザー登録をしています。
登録が完了したら、HTTPステータスは200でjsonレスポンスにメッセージを入れて返すようにしています。

APIでログイン処理を実装する

ログインAPIの実装例と解説を下記に記載しています。
sanctumで認証が必要なエリアにPostmanからアクセスするためトークンを返すようにしてみました。

ログインコントローラーの作成

下記のartisanコマンドで作成できます。

php artisan make:controller LoginController

作成後にapp\Http\Controllers\Apiに移動しました。

<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Validation\ValidationException;
use App\Models\User;
use \Symfony\Component\HttpFoundation\Response;

class LoginController extends Controller
{
    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => 'required|email',
            'password' => 'required'
        ]);

        if (Auth::attempt($credentials)) {
            $user = User::whereEmail($request->email)->first();

            $user->tokens()->delete();
            $token = $user->createToken("login:user{$user->id}")->plainTextToken;

            return response()->json(['token' => $token ], Response::HTTP_OK);
        }

        return response()->json('User Not Found.', Response::HTTP_INTERNAL_SERVER_ERROR);
    }
}

解説

まず、バリデーションを行っています。
問題なければ下記の行にて、ログイン処理を行います。

if (Auth::attempt($credentials)) {

成功したらtrueが返ってきます。
成功した場合は、userのデータを取得して、userのトークンを一度削除します。

その後に下記で、トークンの作成と取得をしています。

$token = $user->createToken("login:user{$user->id}")->plainTextToken;

トークンはマイグレーションで作成したpersonal_access_tokensテーブルに作成されます。
トークンを取得したら、ステータス200でjsonレスポンスにトークンを入れて返します。

今回はPostmanからテストするためトークンも作成してみました。
ログインできない場合(ユーザー情報が間違っている)は、ステータス500で、’User Not Found’というメッセージを返すようにしました。

ルーティング(routes/api.php)を修正する

作成したログインAPIと登録用APIに対して、ルートを作成します。

api.phpファイルを修正する

下記のように修正して、それぞれのルートを作成しました。

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\RegisterController;
use App\Http\Controllers\Api\LoginController;

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

// ユーザー登録
Route::post('/register', [RegisterController::class, 'register']);

// ログイン
Route::post('/login', [LoginController::class, 'login']);

解説

Route::middleware('auth:sanctum')から始まっているルートが、認証されていないとアクセスできない場所になります。
グループ化して複数定義する場合は、Route::middleware('auth:sanctum')->groupを使うとグループ化して、複数のルートを定義できます。

最初に定義しているのはユーザー情報を返すURLです。sanctumで認証されていないとアクセスできません。
画面からログインしてアクセスするか、トークンをAPIにつけることでアクセスできます。

次にユーザー登録APIとログインAPIのルートを定義しています。
それぞれ、post形式で送信される想定です。

動作確認

バックエンドでのユーザー登録と認証処理の作成が完了したので、動作確認をPostmanを使用して行ってみます。

ユーザー登録を行ってみます。
登録するユーザー情報(name,email,password)をjson形式にして、http://127.0.0.1:8090/api/registerにリクエストします。

データベースを確認するとユーザーが登録できています!

ログイン処理を行ってみます。
登録したユーザーのemailとパスワードをjson形式にして、http://127.0.0.1:8090/api/loginにリクエストします。

ログインが完了して、認証エリアにアクセスするためのトークンが返ってきました。
直接URLにアクセスして、認証エリアでの処理をする時にはトークンをHeaderのAuthorizetionのBearerにつける必要があります。

通常通り、SPA画面からログインして、登録などの処理をする場合にはトークンは必要ないようです。
Cookieがついてくるので、それで判断してくれるようです。

最後に認証エリアへのアクセスを行ってみます。
Route::middleware('auth:sanctum')から始まっている下記が、認証されているか、トークンがないとアクセスできないurlです。

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

tokenをAuthorizationタブに設定します。typeとしてBearer Tokenを選びます。
ログイン時に取得したトークンを付加して、リクエストします。

ログインできてユーザー情報が返ってきました👍

終わりに

レスポンスの共通化など、いろいろ直すところはありますが、sanctumを使用した検証ができました。
あとは、PostmanでのリクエストをNuxtなどのSPAから書いていくという流れになります。
後から気付きましたが、Laravel8を使っているからLaravel Jetstreamを使用してみても良かったかも…😅

次回、フロントエンドの処理を書いてみたいと思います。
今回検証用としてトークンを作成している箇所は削除しましたが、コードをここに置いています。
(masterへ1/13にコミットしているコードです)

コメント

タイトルとURLをコピーしました