LoginSignup
8
5

More than 1 year has passed since last update.

Laravel8 マルチログイン時のメール認証

Posted at

初めに

Laravel8でマルチログイン、メール認証それぞれの単体記事はたくさんあるのですが、マルチログインを実装した際のメール認証がなく私自身が少し詰まったので、まとめようと思います。初めての記事投稿ですので、お手柔らかに🙇‍♂️

環境

PHP: 8.0.9
Laravel: 8.52.0
laravel/breeze: 1.4

準備

マルチログインの実装に関する記事はたくさんあるので、マルチログイン実装部分は割愛させていただきます。
ModelはUserとAdminを作成して、prefixとasをつけています。

RouteServiceProvider.php
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::prefix('admin')
                ->as('admin.')
                ->middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/admin.php'));

            Route::prefix('/')
                ->as('user.')
                ->middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));
        });
    }

実装

まずは、ドキュメントを確認します。
Laravel8では簡単にメール認証を組み込む方法を用意してくれています。

ドキュメント通りに実装し、いざ新規登録をしてみると、、、

スクリーンショット 2022-01-16 17.16.49.png

先ほど、RouteServiceProvider.phpでasを指定したため、web.php, auth.phpのルートは先頭に(user.)がつきます。
そのため、verification.verifyが定義されてないよと怒られてしまいます。

また、以下の部分でパスを指定しています。

vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.php
 protected function verificationUrl($notifiable)
    {
        if (static::$createUrlCallback) {
            return call_user_func(static::$createUrlCallback, $notifiable);
        }
        return URL::temporarySignedRoute(
            'verification.verify', ここ

            Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
            [
                'id' => $notifiable->getKey(),

                'hash' => sha1($notifiable->getEmailForVerification()),
            ]
        );
    }

vendor配下のコードを直接変更するのはよろしくないので、このクラスをオーバーライドしていきます。

メール通知なのでNotification配下に作成します。vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.phpの中身を作成したVerifyEmailにまるっとコピーしてきて、パスを指定している部分を変更します。

 php artisan make:notification User/VerifyEmail
app/Notifications/User/VerifyEmail.php
<?php

namespace App\Notifications\User;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;

class VerifyEmail extends Notification
{
    use Queueable;

        /**
     * The callback that should be used to create the verify email URL.
     *
     * @var \Closure|null
     */
    public static $createUrlCallback;

    /**
     * The callback that should be used to build the mail message.
     *
     * @var \Closure|null
     */
    public static $toMailCallback;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Build the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        $verificationUrl = $this->verificationUrl($notifiable);

        if (static::$toMailCallback) {
            return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
        }

        return $this->buildMailMessage($verificationUrl);
    }

        /**
     * Get the verify email notification mail message for the given URL.
     *
     * @param  string  $url
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    protected function buildMailMessage($url)
    {
        return (new MailMessage)
            ->subject(Lang::get('Verify Email Address'))
            ->line(Lang::get('Please click the button below to verify your email address.'))
            ->action(Lang::get('Verify Email Address'), $url)
            ->line(Lang::get('If you did not create an account, no further action is required.'));
    }

        /**
     * Get the verification URL for the given notifiable.
     *
     * @param  mixed  $notifiable
     * @return string
     */
    protected function verificationUrl($notifiable)
    {
        if (static::$createUrlCallback) {
            return call_user_func(static::$createUrlCallback, $notifiable);
        }

        return URL::temporarySignedRoute(
            'user.verification.verify',// ←ここ
            Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
            [
                'id' => $notifiable->getKey(),
                'hash' => sha1($notifiable->getEmailForVerification()),
            ]
        );
    }

        /**
     * Set a callback that should be used when creating the email verification URL.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function createUrlUsing($callback)
    {
        static::$createUrlCallback = $callback;
    }

    /**
     * Set a callback that should be used when building the notification mail message.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function toMailUsing($callback)
    {
        static::$toMailCallback = $callback;
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

そして以下を追加。これは、MustVerifyEmail内のメソッドで新規登録時に送信されるメールの処理です。

Models/User.php
public function sendEmailVerificationNotification(){
   $this->notify(new VerifyEmail());
}

これでいける!!!
と思いきや、
スクリーンショット 2022-01-16 18.27.57.png

これは、middlewareのEnsureEmailIsVerifiedで先ほどと同じようにバスを指定している部分でエラーが発生しています。
web.phpなどで指定しているverifiedですね。

app\Http\Kernel.php
...省略
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];
web.php
Route::get('/dashboard', function () {
    return view('user.dashboard');
})->middleware(['auth:users', 'verified'])->name('dashboard');

app\Http\Middleware\EnsureEmailIsVerified.phpを作成し、\Illuminate\Auth\Middleware\EnsureEmailIsVerifiedをまるっとコピーします。namespaceは変更する必要があります。get_classでオブジェクトのクラス名を取得して条件分岐。

CustomEnsureEmailIsVerified.php
<?php

namespace App\Http\Middleware; ここ

use Closure;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\URL;

class CustomEnsureEmailIsVerified
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $redirectToRoute
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|null
     */
    public function handle($request, Closure $next, $redirectToRoute = null)
    {
        if (! $request->user() ||
            ($request->user() instanceof MustVerifyEmail &&
            ! $request->user()->hasVerifiedEmail())) {


                if(get_class($request->user()) === 'App\Models\User') { ここ
                    $path = 'user.';
                } else {
                    $path = 'admin.';
                }
            return $request->expectsJson()
                    ? abort(403, 'Your email address is not verified.')
                    : Redirect::guest(URL::route($redirectToRoute ?: $path. 'verification.notice'));
        }

        return $next($request);
    }
}

あとは送られてきたメールからアクセスし、ログインできれば完了です!!
もう一つのモデルも同じように作成すればOKです。

スクリーンショット 2022-01-18 6.18.34.png

参考

もし間違っている点や、もっと良い書き方などあればぜひ教えてください🙇‍♂️

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5