fluid_27’s blog

勉強した内容をアウトプットするためのブログ

マルチログインを実装したLaravelアプリでユーザー登録時に認証メールが送られるようにする

Laravel Breezeではユーザー登録時に認証メールが送られるようにする機能が備わっており、簡単に実装できるようです。

 

が、現在BtoCのアプリを作成しており、マルチログイン(ユーザー、企業、管理者など、それぞれでログインできる)を実装している関係で、認証メール実装するまでに少し時間がかかりました。

 

https://qiita.com/may_nkn/items/b396bb211c66ade07adf

主に上記の記事を見ながら実装してみたのですが、記事の通りやってもうまくいかなかった箇所もあったので、追加で行った処理も記載し、備忘録として実装までの道筋をまとめておこうと思います。

 

 

1. 認証メールが送られるようにMustVerifyEmail追加

マルチログインではなく、ただ認証メールが送信されるようにするのは簡単で、

User.phpでUserクラスに対してMustVerifyEmailインターフェースを指定するだけで実現できます。

 

app/Models/User.php

<?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;
 
-class User extends Authenticatable
+class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable;

 

が、マルチログインだと多少コードを修正していく必要があります。

 

2.  以下コマンドでVerifyEmail.phpを作成

php artisan make:notification User/VerifyEmail

 

作成されたVerifyEmail.php

vendor\laravel\framework\src\Illuminate\Auth\Notifications\VerifyEmail.phpの内容をコピー。

元記事でも説明されてますが、マルチログイン実装している関係でrouteを記載している箇所だけ修正する必要があります。

vendor配下のコードを直接修正するのは良くないので、user配下にVerifyEmailを作成し、そちらを修正する手順をとっています。

 

app\Notifications\User\VerifyEmail.php

protected function verificationUrl($notifiable)
    {
        if (static::$createUrlCallback) {
            return call_user_func(static::$createUrlCallback, $notifiable);
        }

        return URL::temporarySignedRoute(

-           ‘verification.verify’,
+           ‘user.verification.verify’,
            Carbon::now()->addMinutes(Config::get(‘auth.verification.expire’, 60)),
            [
                ‘id’ => $notifiable->getKey(),
                ‘hash’ => sha1($notifiable->getEmailForVerification()),
            ]
        );
    }

 

 

3. User.phpでsendEmailVerificationNotificationをオーバーライド

app/Models/User.php

<?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;
use Illuminate\Database\Eloquent\SoftDeletes;
+ use App\Notifications\User\VerifyEmail;

class User extends Authenticatable implements MustVerifyEmail
{
    use HasApiTokens, HasFactory, Notifiable;
    use SoftDeletes;

+    public function sendEmailVerificationNotification()
+    {
+        $this->notify(new VerifyEmail());
+    }
}

 

こちらは元は

vendor\laravel\framework\src\Illuminate\Contracts\Auth\MustVerifyEmail.php

の中で定義されているメソッドです。

また、use文で先ほど生成したVerifyEmailを呼び出せるようにしておきます。

 

ちなみに、上記メソッドは

app\Http\Controllers\Auth\EmailVerificationNotificationController.phpの中で呼ばれていました。

<?php

namespace App\Http\Controllers\User\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;

class EmailVerificationNotificationController extends Controller
{
    public function store(Request $request)
    {
        if ($request->user()->hasVerifiedEmail()) {
            return redirect()->intended(RouteServiceProvider::HOME);
        }

        $request->user()->sendEmailVerificationNotification();

        return back()->with(‘status’, ‘verification-link-sent’);
    }
}

 

4. app\Http\Middleware\EnsureEmailIsVerified.phpを作成し、vendor\laravel\framework\src\Illuminate\Auth\Middleware\EnsureEmailIsVerifiedの内容をコピー

 

こちらもマルチログインの関係でコードを修正する必要があるのですが、先ほどと同様vendor配下のコードを修正するのは良くないので、app配下のMiddlewareに新規でEnsureEmailsVerified.phpを作成し、そちらコピーしてから修正します。

 

app\Http\Middleware\EnsureEmailIsVerified.php

<?php

namespace App\Http\Middleware;

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

class EnsureEmailIsVerified
{
  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.’;
+      } elseif (get_class($request->user()) === ‘App\Models\Companies’) {
+        $path = ‘company.’;
+      }
      return $request->expectsJson()
        ? abort(403, ‘Your email address is not verified.’)

-        : Redirect::guest(URL::route($redirectToRoute ?:  ‘verification.notice’));
+        : Redirect::guest(URL::route($redirectToRoute ?: $path . ‘verification.notice’));
    }

    return $next($request);
  }
}

上記のif文でUserクラスなのかCompaniesクラスなのかで条件分岐してrouteのパスが

'verification.notice' でなく

'user.verification.notice' や 'company.verification.notice' になるようにします。

(もちろん、環境によりrouteのパスは違うと思いますが)

 

5. kernel.phpの内容を変更

routes配下でルーティングを定義する際に'verified'をmiddlewareとして指定すれば、メール認証済みかどうかをチェックしてくれますが、元の設定ではvendor配下のEnsureEmailsVerifiedが呼ばれてしまうので、上記で生成した方を呼び出すよう

Kernel.phpを修正。

 

app\Http\Kernel.php

protected $routeMiddleware = [
        ‘auth’ => \App\Http\Middleware\Authenticate::class,
        ‘auth.basic’ => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        ‘cache.headers’ => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        ‘can’ => \Illuminate\Auth\Middleware\Authorize::class,
        ‘guest’ => \App\Http\Middleware\RedirectIfAuthenticated::class,
        ‘password.confirm’ => \Illuminate\Auth\Middleware\RequirePassword::class,
        ‘signed’ => \Illuminate\Routing\Middleware\ValidateSignature::class,
        ‘throttle’ => \Illuminate\Routing\Middleware\ThrottleRequests::class,
-         ‘verified’ => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+        ‘verified’ => \App\Http\Middleware\EnsureEmailIsVerified::class,

 

6. ルーティングでverifiedを適用

後は任意のルートにmiddlewareで'verified'を指定すれば、メール認証が済みかどうかチェックしてくれるようになります。

 

routes\web.php

Route::resource(‘user’, UserController::class, [‘except’ => ‘index’])->middleware([‘auth:users’, ‘verified’]);

などと指定すると、上記のルーティング先でメール認証済みかどうかチェックされ、認証済みでないと

resources\views\auth\verify-email.blade.phpが読み込まれ、下記のように表示されます。

 

 

間違い等あればご指摘お願いします。