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.php
とapp/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にコミットしているコードです)
コメント