• トップ
  • ブログ一覧
  • Jetstreamでのマルチ認証について
  • Jetstreamでのマルチ認証について

    たなゆー(エンジニア)たなゆー(エンジニア)
    2021.12.16

    IT技術

    Jetstreamを使って2種類のユーザー認証をする

    今回はJerstreamを使って2種類のユーザーを認証していきます。

    具体的には User と Worker の2種類のユーザーを作成して認証します。

    Jestreamをインストール

    まずはJetstreamをインストールしていきます。

    私の場合既に立ち上げたアプリにJetstreamをインストールしたので、

    以下のようなコマンドでインストール

    1$ composer require laravel/jetstream
    2$ php artisan jetstream:install livewire

    Jetstreamにはteamsというチーム機能をサポートするものがありますが今回は使用しません。

    (マルチ認証の際に少し悪さをしたため)

    Jetstreamをインストールした後は以下のコマンドで環境を構築し、DBのマイグレートまで行いましょう。

    1npm install && npm run dev
    2php artisan migrate

    これでJetstreamを使う準備は整いました。

    認証機能の実装に進みましょう。

    マルチ認証の実装

    Jetstreamでは標準でUserの認証機能が実装されています。

    Workerの認証機能はこのUserの機能をコピーして一部修正するような形で実現していきます。

    まずは WorkerLoginResponse.php を作成していきます。

    これは vender/laravel/fortify/src/Http/Responses/LoginResponse.php を参考に作成していきます。

    app/Responseディレクトリを作成しその中にWorkerLoginResponse.phpを作成します。

    1<?php
    2
    3namespace App\Responses;
    4
    5use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
    6
    7class WorkerLoginResponse implements LoginResponseContract
    8{
    9    /*
    10     * Create an HTTP response that represents the object.
    11     *
    12     * @param  \Illuminate\Http\Request  $request
    13     * @return \Symfony\Component\HttpFoundation\Response
    14     */
    15    public function toResponse($request)
    16    {
    17        return $request->wantsJson()
    18            ? response()->json(['two_factor' => false])
    19            : redirect()->intended('worker/dashboard'); // ログイン後に遷移させたいリダイレクト先を指定
    20    }
    21}

    ここでWorker用のログイン画面も作成しておきましょう、と言ってもここでは標準で実装されているlogin.blade.phpを修正する形を取ります。

    issetでguard変数の有無を判別してURLを分けています。

    次にapp/Actions/Workerディレクトリを作成しAttemptToAuthenticate.phpを作成します。

    こちらはvendor/laravel/fortify/src/Actions/AttemptToAuthenticate.phpを参考に作成します。

    1<?php
    2
    3namespace App\Actions\Worker;
    4
    5use Illuminate\Contracts\Auth\StatefulGuard;
    6use Illuminate\Validation\ValidationException;
    7use Laravel\Fortify\Fortify;
    8use Laravel\Fortify\LoginRateLimiter;
    9
    10class AttemptToAuthenticate
    11{
    12    /*
    13     * The guard implementation.
    14     *
    15     * @var \Illuminate\Contracts\Auth\StatefulGuard
    16     */
    17    protected $guard;
    18
    19    /**
    20     * The login rate limiter instance.
    21     *
    22     * @var \Laravel\Fortify\LoginRateLimiter
    23     */
    24    protected $limiter;
    25
    26    /**
    27     * Create a new controller instance.
    28     *
    29     * @param  \Illuminate\Contracts\Auth\StatefulGuard  $guard
    30     * @param  \Laravel\Fortify\LoginRateLimiter  $limiter
    31     * @return void
    32     */
    33    public function __construct(StatefulGuard $guard, LoginRateLimiter $limiter)
    34    {
    35        $this->guard = $guard;
    36        $this->limiter = $limiter;
    37    }
    38
    39    /**
    40     * Handle the incoming request.
    41     *
    42     * @param  \Illuminate\Http\Request  $request
    43     * @param  callable  $next
    44     * @return mixed
    45     */
    46    public function handle($request, $next)
    47    {
    48        if (Fortify::$authenticateUsingCallback) {
    49            return $this->handleUsingCustomCallback($request, $next);
    50        }
    51
    52        if ($this->guard->attempt(
    53            $request->only(Fortify::username(), 'password'),
    54            $request->filled('remember'))
    55        ) {
    56            return $next($request);
    57        }
    58
    59        $this->throwFailedAuthenticationException($request);
    60    }
    61
    62    /**
    63     * Attempt to authenticate using a custom callback.
    64     *
    65     * @param  \Illuminate\Http\Request  $request
    66     * @param  callable  $next
    67     * @return mixed
    68     */
    69    protected function handleUsingCustomCallback($request, $next)
    70    {
    71        $user = call_user_func(Fortify::$authenticateUsingCallback, $request);
    72
    73        if (! $user) {
    74            return $this->throwFailedAuthenticationException($request);
    75        }
    76
    77        $this->guard->login($user, $request->filled('remember'));
    78
    79        return $next($request);
    80    }
    81
    82    /**
    83     * Throw a failed authentication validation exception.
    84     *
    85     * @param  \Illuminate\Http\Request  $request
    86     * @return void
    87     *
    88     * @throws \Illuminate\Validation\ValidationException
    89     */
    90    protected function throwFailedAuthenticationException($request)
    91    {
    92        $this->limiter->increment($request);
    93
    94        throw ValidationException::withMessages([
    95            Fortify::username() => [trans('auth.failed')],
    96        ]);
    97    }
    98}

    ここで作成したAttemptToAuthenticate.phpとのちに作成するLoginControllerで使用するWorkerLoginServiceProvider.phpを作成します。

    1<?php
    2
    3namespace App\Providers;
    4
    5use App\Http\Controllers\Auth\WorkerLoginController;
    6use Illuminate\Contracts\Auth\StatefulGuard;
    7use Illuminate\Support\Facades\Auth;
    8use App\Actions\Worker\AttemptToAuthenticate;
    9use Illuminate\Support\ServiceProvider;
    10
    11class WorkerLoginServiceProvider extends ServiceProvider
    12{
    13    /*
    14     * Register services.
    15     *
    16     * @return void
    17     */
    18    public function register()
    19    {
    20        $this->app
    21            ->when([WorkerLoginController::class, AttemptToAuthenticate::class])
    22            ->needs(StatefulGuard::class)
    23            ->give(function () {
    24                return Auth::guard('worker');
    25            });
    26    }
    27
    28    /**
    29     * Bootstrap services.
    30     *
    31     * @return void
    32     */
    33    public function boot()
    34    {
    35        //
    36    }
    37}

    たった今作成したWorkerLoginServiceProvider.phpが反映されるようにapp.phpを修正します。

    1        〜省略〜
    2         /*
    3         * Application Service Providers...
    4         */
    5        App\Providers\AppServiceProvider::class,
    6        App\Providers\AuthServiceProvider::class,
    7        // App\Providers\BroadcastServiceProvider::class,
    8        App\Providers\EventServiceProvider::class,
    9        App\Providers\RouteServiceProvider::class,
    10        App\Providers\FortifyServiceProvider::class,
    11        App\Providers\JetstreamServiceProvider::class,
    12        App\Providers\WorkerLoginServiceProvider::class, //この行を追加
    13        App\Providers\CompanyLoginServiceProvider::class,
    14
    15       〜省略〜

    ここでWorker認証用のWorkerLoginControllerを作成していきます。

    1$ php artisan make:controller Auth/LoginController

    vendor/laravel/fortify/src/Http/Controllers/AuthenticatedSessionController.php を参考に WorkerLoginControllerを作成。

    1<?php
    2
    3namespace App\Http\Controllers\Auth;
    4
    5use App\Http\Controllers\Controller;
    6use Illuminate\Contracts\Auth\StatefulGuard;
    7use Illuminate\Http\Request;
    8use Illuminate\Routing\Pipeline;
    9use App\Actions\Worker\AttemptToAuthenticate;
    10use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
    11use App\Responses\WorkerLoginResponse;
    12use Laravel\Fortify\Contracts\LogoutResponse;
    13use Laravel\Fortify\Http\Requests\LoginRequest;
    14
    15class workerLoginController extends Controller
    16{
    17    /*
    18     * The guard implementation.
    19     *
    20     * @var \Illuminate\Contracts\Auth\StatefulGuard
    21     */
    22    protected $guard;
    23
    24    /**
    25     * Create a new controller instance.
    26     *
    27     * @param  \Illuminate\Contracts\Auth\StatefulGuard
    28     * @return void
    29     */
    30    public function __construct(StatefulGuard $guard)
    31    {
    32        $this->guard = $guard;
    33    }
    34
    35    /**
    36     * Show the login view.
    37     *
    38     * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
    39     */
    40    public function create()
    41    {
    42        return view('auth.login', ['guard' => 'worker']);
    43    }

    Workerとしてログインした後のダッシュボード表示用にWorkerDashboardController.phpとWorker/dashboard.blade.phpを作成していきます。

    1$ php artisan make:controller WorkerDashboardController
    1<?php
    2
    3namespace App\Http\Controllers;
    4
    5use Illuminate\Http\Request;
    6use App\Models\Worker;
    7
    8class WorkerDashboardController extends Controller
    9{
    10    public function index()
    11    {
    12        $workers = Worker::all();
    13
    14        return view('worker.dashboard', ['workers' => $workers,]);
    15    }
    16}
    1<x-app-layout>
    2    <x-slot name="header">
    3        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
    4            <!-- わかりやすいようにここを修正-->
    5            Workerダッシュボード
    6        </h2>
    7    </x-slot>
    8
    9    <div class="py-12">
    10        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
    11            <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
    12                <x-jet-welcome />
    13            </div>
    14        </div>
    15    </div>
    16
    17    <div class="company-list">
    18        @foreach($workers as $worker)
    19        <a href="{{ route('workers.profile', ['id' => $worker->id]) }}">
    20                <p>{{ $worker->name }}</p>
    21            </a>
    22            <p>{{ $worker->experience_year }}</p>
    23            <p>{{ $worker->area }}</p>
    24        @endforeach
    25    </div>
    26</x-app-layout>

    また未ログイン時にダッシュボードにアクセスした時にログイン画面にリダイレクトされるようAuthenticate.phpを編集します。

    1<?php
    2
    3namespace App\Http\Middleware;
    4
    5use Illuminate\Auth\Middleware\Authenticate as Middleware;
    6
    7
    8class Authenticate extends Middleware
    9{
    10    /*
    11     * Get the path the user should be redirected to when they are not authenticated.
    12     *
    13     * @param  \Illuminate\Http\Request  $request
    14     * @return string|null
    15     */
    16    protected function redirectTo($request)
    17    {
    18        if (! $request->expectsJson()) {
    19
    20            if($request->is('worker/*')) {
    21                return route('worker.login');
    22            }
    23            
    24            if($request->is('company/*')) {
    25                return route('company.login');
    26            }
    27
    28            return route('login');
    29        }
    30    }
    31}

    Worker用のログイン画面とダッシュボードのルーティングをしていきます。

    1<?php
    2
    3use App\Http\Controllers\WorkerDashboardController;
    4use App\Http\Controllers\Auth\WorkerLoginController;
    5use App\Http\Controllers\Auth\WorkerRegisterController;
    6use App\Http\Controllers\UserController;
    7use App\Http\Controllers\WorkerController;
    8use Illuminate\Support\Facades\Route;
    9use App\Models\User;
    10
    11// Route::get('/', 'HomeController@index')->name('home');
    12Route::get('/', function () {
    13    return view('welcome');
    14});
    15
    16Route::prefix('worker')->group(function () {
    17    Route::get('login', [WorkerLoginController::class, 'create'])->name('worker.login');
    18    Route::post('login', [WorkerLoginController::class, 'store']);
    19
    20    Route::get('register', [WorkerRegisterController::class, 'create'])->name('worker.register');
    21    Route::post('register', [WorkerRegisterController::class, 'store']);
    22
    23    Route::middleware('auth:worker')->group(function () {
    24        Route::get('dashboard', [WorkerDashboardController::class, 'index']);
    25    });
    26});

    最後に auth.phpにWorler用の認証ガードを定義します。

    1〜省略〜
    2'guards' => [
    3        'web' => [
    4            'driver' => 'session',
    5            'provider' => 'users',
    6        ],
    7
    8        'worker' => [
    9            'driver' => 'session',
    10            'provider' => 'workers',
    11        ],
    12
    13       'api' => [
    14            'driver' => 'token',
    15            'provider' => 'users',
    16            'hash' => false,
    17        ],
    18〜省略〜
    19'providers' => [
    20        'users' => [
    21            'driver' => 'eloquent',
    22            'model' => App\Models\User::class,
    23        ],
    24
    25        'workers' => [
    26            'driver' => 'eloquent',
    27            'model' => App\Models\Worker::class,
    28        ],
    29〜省略〜
    30'passwords' => [
    31        'users' => [
    32            'provider' => 'users',
    33            'table' => 'password_resets',
    34            'expire' => 60,
    35            'throttle' => 60,
    36        ],
    37
    38        'workers' => [
    39            'provider' => 'workers',
    40            'table' => 'password_resets',
    41            'expire' => 60,
    42            'throttle' => 60,
    43        ],

    以上でマルチ認証が完成しました!!

    WorkerとUserの2通りでログインができるようになったかと思います。

    たなゆー(エンジニア)

    たなゆー(エンジニア)

    おすすめ記事

    GitHubActionsのランナーに触れてみた

    こやまん(エンジニア)

    こやまん(エンジニア)

    2024.03.28

    IT技術

    Azure Data FactoryでSlackへ通知をしてみる

    たかやん(エンジニア)

    たかやん(エンジニア)

    2024.03.28

    IT技術

    GCP Secret Managerを使ってみた

    たなゆー(エンジニア)

    たなゆー(エンジニア)

    2024.03.21

    IT技術

    Bitriseのパイプラインと環境変数

    加納(エンジニア)

    加納(エンジニア)

    2024.03.11

    IT技術