Laravel Breezeではユーザー登録時に認証メールが送られるようにする機能が備わっており、簡単に実装できるようです。
が、現在BtoCのアプリを作成しており、マルチログイン(ユーザー、企業、管理者など、それぞれでログインできる)を実装している関係で、認証メール実装するまでに少し時間がかかりました。
https://qiita.com/may_nkn/items/b396bb211c66ade07adf
主に上記の記事を見ながら実装してみたのですが、記事の通りやってもうまくいかなかった箇所もあったので、追加で行った処理も記載し、備忘録として実装までの道筋をまとめておこうと思います。
- 1. 認証メールが送られるようにMustVerifyEmail追加
- 2. 以下コマンドでVerifyEmail.phpを作成
- 3. User.phpでsendEmailVerificationNotificationをオーバーライド
- 4. app\Http\Middleware\EnsureEmailIsVerified.phpを作成し、vendor\laravel\framework\src\Illuminate\Auth\Middleware\EnsureEmailIsVerifiedの内容をコピー
- 5. kernel.phpの内容を変更
- 6. ルーティングでverifiedを適用
1. 認証メールが送られるようにMustVerifyEmail追加
マルチログインではなく、ただ認証メールが送信されるようにするのは簡単で、
User.phpでUserクラスに対してMustVerifyEmailインターフェースを指定するだけで実現できます。
<?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を作成
作成された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が読み込まれ、下記のように表示されます。
間違い等あればご指摘お願いします。