Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Laravel のメール認証の内部実装を掘り下げる - PHPerKaigi 2021

Laravel のメール認証の内部実装を掘り下げる - PHPerKaigi 2021

スライド内で紹介した設計方針で実装したサンプルコードも公開しています。
興味のある方はこちらも是非ご覧ください!

https://github.com/hamakou108/laravel-separate-user-sample

1ac5890739d082cd2f2f91e3c1799b4d?s=128

hamakou108

March 27, 2021
Tweet

Transcript

  1. Laravel のメール認証の 内部実装を掘り下げる 2021.03.27 PHPerKaigi 2021 株式会社 M&A クラウド 濱⽥晃輔

    @ hamakou108
  2. ⾃⼰紹介 濱⽥晃輔 ( ) 株式会社M&A クラウド Web エンジニア PHP, Laravel

    TypeScript, Vue.js, Nuxt.js AWS Vim @hamakou108
  3. None
  4. None
  5. None
  6. このスライドで話すこと メール認証の仕組みと構成要素 Laravel を使ったメール認証の実装⽅法 フレームワークの内部実装を理解して拡張する

  7. このスライドで話さないこと メール認証のセキュリティ上の利点や⽋点 Laravel の基本的な機能の説明 Service Container, Service Provider Authentication, Authorization

    Middleware Routing Eloquent, Query Builder Notifications etc. トークンの⽣成ロジックなどの実装の詳細部分
  8. メール認証の仕組みとその構成要素

  9. メール認証とは ⼊⼒・登録されたメールアドレスを使った認証⽅法 ⼀時的に有効なトークンつき URL を記載したメールを送信し、その URL の閲 覧を以って本⼈性を担保する 他⼈のメールアドレスを⽤いた成りすましのリスクを下げる

  10. シーケンス図で構成要素を洗う

  11. シーケンス図で構成要素を洗う

  12. シーケンス図で構成要素を洗う

  13. シーケンス図で構成要素を洗う

  14. その他の構成要素 認証 URL (トークン)を⽣成する ユーザーの認証情報を埋め込む トークンの有効期限を埋め込む 改ざん防⽌のため署名を埋め込む 認証 URL (トークン)へのリクエストを検証する

    認証メールの再送機能を⽤意する
  15. まるっと実装するのは結構⼤変そう🥺

  16. Laravel を使ったメール認証の実装⽅法

  17. Laravel の利⽤者がやること App User (App\Models\User) に MustVerifyEmail インターフェースを実装させる 本⼈認証が必要なページの Routing

    を Middleware で保護する Starter kits で Routing や Controller や view などを⽣成する
  18. App User App\Models\User MustVerifyEmail を実装する = メール認証の有効化 ユーザー登録時に認証メールを送信する 認証 URL

    へのリクエストを検証する 認証⽇時を保存する ユーザー認証済みかどうかを検証する use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable implements MustVerifyEmail { use Notifiable; // ... } 1 2 3 4 5 6 7 8 9
  19. App User App\Models\User MustVerifyEmail を実装する = メール認証の有効化 ユーザー登録時に認証メールを送信する 認証 URL

    へのリクエストを検証する 認証⽇時を保存する ユーザー認証済みかどうかを検証する use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable implements MustVerifyEmail { use Notifiable; // ... } 1 2 3 4 5 6 7 8 9 class User extends Authenticatable implements MustVerifyEmail use Illuminate\Contracts\Auth\MustVerifyEmail; 1 use Illuminate\Foundation\Auth\User as Authenticatable; 2 3 4 { 5 use Notifiable; 6 7 // ... 8 } 9
  20. Routing の保護 Kernel の $routeMiddleware に verified という名前で登録されている。 Route::get('profile', function

    () { // Only verified users may enter... })->middleware('verified'); 1 2 3
  21. Starter kits (Laravel Breeze) を使うと さらに実装量が減る ├── app │ ├──

    Models │ ├── Http │ │ ├── Controllers │ │ └── Middleware │ └── Providers ├── routes ├── resources │ ├── css │ ├── js │ └── views ├── database │ └── migrations └── tests 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  22. Starter kits (Laravel Breeze) を使うと さらに実装量が減る ├── app │ ├──

    Models │ ├── Http │ │ ├── Controllers │ │ └── Middleware │ └── Providers ├── routes ├── resources │ ├── css │ ├── js │ └── views ├── database │ └── migrations └── tests 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ├── app │ ├── Models │ ├── Http │ │ ├── Controllers ├── routes ├── resources │ ├── css │ ├── js │ └── views └── tests 1 2 3 4 │ │ └── Middleware 5 │ └── Providers 6 7 8 9 10 11 ├── database 12 │ └── migrations 13 14
  23. Starter kits (Laravel Breeze) を使うと さらに実装量が減る ├── app │ ├──

    Models │ ├── Http │ │ ├── Controllers │ │ └── Middleware │ └── Providers ├── routes ├── resources │ ├── css │ ├── js │ └── views ├── database │ └── migrations └── tests 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ├── app │ ├── Models │ ├── Http │ │ ├── Controllers ├── routes ├── resources │ ├── css │ ├── js │ └── views └── tests 1 2 3 4 │ │ └── Middleware 5 │ └── Providers 6 7 8 9 10 11 ├── database 12 │ └── migrations 13 14 ├── app │ ├── Models ├── routes 1 2 │ ├── Http 3 │ │ ├── Controllers 4 │ │ └── Middleware 5 │ └── Providers 6 7 ├── resources 8 │ ├── css 9 │ ├── js 10 │ └── views 11 ├── database 12 │ └── migrations 13 └── tests 14
  24. Starter kits (Laravel Breeze) を使うと さらに実装量が減る ├── app │ ├──

    Models <- MustVerifyEmail を実装するだけ ├── routes <- middleware('verified') を追加するだけ 1 2 │ ├── Http 3 │ │ ├── Controllers 4 │ │ └── Middleware 5 │ └── Providers 6 7 ├── resources 8 │ ├── css 9 │ ├── js 10 │ └── views 11 ├── database 12 │ └── migrations 13 └── tests 14
  25. Laravel の機能やツールを活⽤すれば 少ない⼿数でメール認証を実装できる 😃

  26. フレームワークの内部実装を理解して拡張する

  27. Laravel のメール認証は便利だが ... フレームワーク内部で何をしているかよく分からない ドキュメントには必要最低限の拡張⽅法しか書かれていない 例えば RDB を NoSQL に差し替えたい場合はどうする?

  28. App User (再掲) App\Models\User use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable;

    class User extends Authenticatable implements MustVerifyEmail { use Notifiable; // ... } 1 2 3 4 5 6 7 8 9
  29. 依存先が⾒えるように書き直してみる Auth 関連の Contract とトレイト、 Eloquent Model 、 Notifiable に依存してい

    る use Illuminate\Auth\Authenticatable; use Illuminate\Auth\MustVerifyEmail; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Notifications\Notifiable; class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Notifiable, Authenticatable 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  30. 依存先が⾒えるように書き直してみる Auth 関連の Contract とトレイト、 Eloquent Model 、 Notifiable に依存してい

    る use Illuminate\Auth\Authenticatable; use Illuminate\Auth\MustVerifyEmail; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Notifications\Notifiable; class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Notifiable, Authenticatable 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Database\Eloquent\Model; 8 use Illuminate\Foundation\Auth\Access\Authorizable; 9 use Illuminate\Notifications\Notifiable; 10 11 class User extends Model implements 12 13 14 15 16 { 17 use Notifiable, 18 19 20 21 22 } 23
  31. 依存先が⾒えるように書き直してみる Auth 関連の Contract とトレイト、 Eloquent Model 、 Notifiable に依存してい

    る use Illuminate\Auth\Authenticatable; use Illuminate\Auth\MustVerifyEmail; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Notifications\Notifiable; class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Notifiable, Authenticatable 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract Authenticatable use Illuminate\Auth\Authenticatable; 1 use Illuminate\Auth\MustVerifyEmail; 2 use Illuminate\Auth\Passwords\CanResetPassword; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Database\Eloquent\Model; 8 use Illuminate\Foundation\Auth\Access\Authorizable; 9 use Illuminate\Notifications\Notifiable; 10 11 class User extends Model implements 12 13 14 15 16 { 17 use Notifiable, 18 19 use Illuminate\Database\Eloquent\Model; class User extends Model implements use Illuminate\Auth\Authenticatable; 1 use Illuminate\Auth\MustVerifyEmail; 2 use Illuminate\Auth\Passwords\CanResetPassword; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 8 use Illuminate\Foundation\Auth\Access\Authorizable; 9 use Illuminate\Notifications\Notifiable; 10 11 12 AuthenticatableContract, 13 AuthorizableContract, 14 CanResetPasswordContract, 15 MustVerifyEmailContract 16 { 17 use Notifiable, 18 Authenticatable, 19
  32. 依存先が⾒えるように書き直してみる Auth 関連の Contract とトレイト、 Eloquent Model 、 Notifiable に依存してい

    る use Illuminate\Auth\Authenticatable; use Illuminate\Auth\MustVerifyEmail; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Notifications\Notifiable; class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Notifiable, Authenticatable 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract Authenticatable use Illuminate\Auth\Authenticatable; 1 use Illuminate\Auth\MustVerifyEmail; 2 use Illuminate\Auth\Passwords\CanResetPassword; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Database\Eloquent\Model; 8 use Illuminate\Foundation\Auth\Access\Authorizable; 9 use Illuminate\Notifications\Notifiable; 10 11 class User extends Model implements 12 13 14 15 16 { 17 use Notifiable, 18 19 use Illuminate\Database\Eloquent\Model; class User extends Model implements use Illuminate\Auth\Authenticatable; 1 use Illuminate\Auth\MustVerifyEmail; 2 use Illuminate\Auth\Passwords\CanResetPassword; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 8 use Illuminate\Foundation\Auth\Access\Authorizable; 9 use Illuminate\Notifications\Notifiable; 10 11 12 AuthenticatableContract, 13 AuthorizableContract, 14 CanResetPasswordContract, 15 MustVerifyEmailContract 16 { 17 use Notifiable, 18 Authenticatable 19 use Illuminate\Notifications\Notifiable; use Notifiable, use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Database\Eloquent\Model; 8 use Illuminate\Foundation\Auth\Access\Authorizable; 9 10 11 class User extends Model implements 12 AuthenticatableContract, 13 AuthorizableContract, 14 CanResetPasswordContract, 15 MustVerifyEmailContract 16 { 17 18 Authenticatable, 19 Authorizable, 20 CanResetPassword, 21 MustVerifyEmail; 22 } 23
  33. Auth 関連のトレイトの依存先 Illuminate\Auth\MustVerifyEmail Auth 関連の機能は App User を通して Eloquent と

    Notifiable に複雑に依存して いる trait MustVerifyEmail { /** * Mark the given user's email as verified. * * @return bool */ public function markEmailAsVerified() { return $this->forceFill([ 'email_verified_at' => $this->freshTimestamp(), ])->save(); } // ... /** * Send the email verification notification. * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  34. Auth 関連のトレイトの依存先 Illuminate\Auth\MustVerifyEmail Auth 関連の機能は App User を通して Eloquent と

    Notifiable に複雑に依存して いる trait MustVerifyEmail { /** * Mark the given user's email as verified. * * @return bool */ public function markEmailAsVerified() { return $this->forceFill([ 'email_verified_at' => $this->freshTimestamp(), ])->save(); } // ... /** * Send the email verification notification. * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * Mark the given user's email as verified. * * @return bool */ public function markEmailAsVerified() { return $this->forceFill([ 'email_verified_at' => $this->freshTimestamp(), ])->save(); } trait MustVerifyEmail 1 { 2 3 4 5 6 7 8 9 10 11 12 13 14 // ... 15 16 /** 17 * Send the email verification notification. 18 * 19
  35. Auth 関連のトレイトの依存先 Illuminate\Auth\MustVerifyEmail Auth 関連の機能は App User を通して Eloquent と

    Notifiable に複雑に依存して いる trait MustVerifyEmail { /** * Mark the given user's email as verified. * * @return bool */ public function markEmailAsVerified() { return $this->forceFill([ 'email_verified_at' => $this->freshTimestamp(), ])->save(); } // ... /** * Send the email verification notification. * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * Mark the given user's email as verified. * * @return bool */ public function markEmailAsVerified() { return $this->forceFill([ 'email_verified_at' => $this->freshTimestamp(), ])->save(); } trait MustVerifyEmail 1 { 2 3 4 5 6 7 8 9 10 11 12 13 14 // ... 15 16 /** 17 * Send the email verification notification. 18 * 19 /** * Send the email verification notification. * * @return void */ public function sendEmailVerificationNotification() { $this->notify(new VerifyEmail); } return $this->forceFill([ 10 'email_verified_at' => $this->freshTimestamp(), 11 ])->save(); 12 } 13 14 // ... 15 16 17 18 19 20 21 22 23 24 25 26 // ... 27 } 28
  36. App User の責務が多すぎる Auth 系の機能を使うための I/F を提供する DB を通して認証情報を取得、保存する (=>

    Eloquent Model) ユーザーにメールを送信する (=> Notifiable)
  37. App User の責務が多すぎる Auth 系の機能を使うための I/F を提供する DB を通して認証情報を取得、保存する (=>

    Eloquent Model) ユーザーにメールを送信する (=> Notifiable)
  38. Repository パターンを利⽤して App User の責務を切り離す

  39. クラス図(変更前)

  40. クラス図(変更後)

  41. ⚠ この先、幾つかの User が出てきます 呼称 役割 App User 認証に⽤いる I/F

    を提供する Domain User ドメインのコアロジックを提供する Eloquent User User を永続化するための機能を提供する Notifiable User User に通知を送るための機能を提供する ※ 公式の呼称ではありません。
  42. Domain Repository Domain\User\UserRepository interface UserRepository { public function markEmailAsVerified(UserId $id):

    bool; // ... } 1 2 3 4 5 6
  43. Infra Repository Infra\Repository\Eloquent\UserRepository use Domain\User\UserRepository as UserRepositoryInterface; use Infra\Model\Eloquent\User as

    UserModel; class UserRepository implements UserRepositoryInterface { public function markEmailAsVerified(UserId $id): bool { return UserModel::where('id', $id->rawValue()) ->whereNull('email_verified_at') ->update([ 'email_verified_at' => Date::now(), ]); } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  44. Infra Repository Infra\Repository\Eloquent\UserRepository use Domain\User\UserRepository as UserRepositoryInterface; use Infra\Model\Eloquent\User as

    UserModel; class UserRepository implements UserRepositoryInterface { public function markEmailAsVerified(UserId $id): bool { return UserModel::where('id', $id->rawValue()) ->whereNull('email_verified_at') ->update([ 'email_verified_at' => Date::now(), ]); } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class UserRepository implements UserRepositoryInterface use Domain\User\UserRepository as UserRepositoryInterface; 1 use Infra\Model\Eloquent\User as UserModel; 2 3 4 { 5 public function markEmailAsVerified(UserId $id): bool 6 { 7 return UserModel::where('id', $id->rawValue()) 8 ->whereNull('email_verified_at') 9 ->update([ 10 'email_verified_at' => Date::now(), 11 ]); 12 } 13 14 // ... 15 } 16
  45. Infra Repository Infra\Repository\Eloquent\UserRepository use Domain\User\UserRepository as UserRepositoryInterface; use Infra\Model\Eloquent\User as

    UserModel; class UserRepository implements UserRepositoryInterface { public function markEmailAsVerified(UserId $id): bool { return UserModel::where('id', $id->rawValue()) ->whereNull('email_verified_at') ->update([ 'email_verified_at' => Date::now(), ]); } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class UserRepository implements UserRepositoryInterface use Domain\User\UserRepository as UserRepositoryInterface; 1 use Infra\Model\Eloquent\User as UserModel; 2 3 4 { 5 public function markEmailAsVerified(UserId $id): bool 6 { 7 return UserModel::where('id', $id->rawValue()) 8 ->whereNull('email_verified_at') 9 ->update([ 10 'email_verified_at' => Date::now(), 11 ]); 12 } 13 14 // ... 15 } 16 public function markEmailAsVerified(UserId $id): bool { return UserModel::where('id', $id->rawValue()) ->whereNull('email_verified_at') ->update([ 'email_verified_at' => Date::now(), ]); } use Domain\User\UserRepository as UserRepositoryInterface; 1 use Infra\Model\Eloquent\User as UserModel; 2 3 class UserRepository implements UserRepositoryInterface 4 { 5 6 7 8 9 10 11 12 13 14 // ... 15 } 16
  46. RepositoryServiceProvider App\Providers\RepositoryServiceProvider インフラが変わる場合は Infra の UserRepository を差し替えれば良いだけにな った! use Domain\User\UserRepository;

    use Infra\Repository\Eloquent\UserRepository as EloquentUserRepository; class RepositoryServiceProvider extends ServiceProvider { public function register() { $this->app->bind( UserRepository::class, EloquentUserRepository::class ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13
  47. RepositoryServiceProvider App\Providers\RepositoryServiceProvider インフラが変わる場合は Infra の UserRepository を差し替えれば良いだけにな った! use Domain\User\UserRepository;

    use Infra\Repository\Eloquent\UserRepository as EloquentUserRepository; class RepositoryServiceProvider extends ServiceProvider { public function register() { $this->app->bind( UserRepository::class, EloquentUserRepository::class ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 $this->app->bind( UserRepository::class, EloquentUserRepository::class ); use Domain\User\UserRepository; 1 use Infra\Repository\Eloquent\UserRepository as EloquentUserRepository; 2 3 class RepositoryServiceProvider extends ServiceProvider 4 { 5 public function register() 6 { 7 8 9 10 11 } 12 } 13
  48. Domain Notifier Domain\User\UserNotifier interface UserNotifier { public function sendEmailVerificationNotification( User

    $user, string $url ): void; } 1 2 3 4 5 6 7
  49. Infra Notifier Infra\Notifier\Illuminate\UserNotifier use Domain\User\User; use Domain\User\UserNotifier as UserNotifierInterface; use

    Illuminate\Auth\Notifications\VerifyEmail; use Infra\Notifiable\User as NotifiableUser; use Infra\Notifications\VerifyEmail; class UserNotifier implements UserNotifierInterface { private Dispatcher $dispatcher; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } public function sendEmailVerificationNotification( User $user, string $url ): void { $this >dispatcher >send( 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  50. Infra Notifier Infra\Notifier\Illuminate\UserNotifier use Domain\User\User; use Domain\User\UserNotifier as UserNotifierInterface; use

    Illuminate\Auth\Notifications\VerifyEmail; use Infra\Notifiable\User as NotifiableUser; use Infra\Notifications\VerifyEmail; class UserNotifier implements UserNotifierInterface { private Dispatcher $dispatcher; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } public function sendEmailVerificationNotification( User $user, string $url ): void { $this >dispatcher >send( 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class UserNotifier implements UserNotifierInterface use Domain\User\User; 1 use Domain\User\UserNotifier as UserNotifierInterface; 2 use Illuminate\Auth\Notifications\VerifyEmail; 3 use Infra\Notifiable\User as NotifiableUser; 4 use Infra\Notifications\VerifyEmail; 5 6 7 { 8 private Dispatcher $dispatcher; 9 10 public function __construct(Dispatcher $dispatcher) { 11 $this->dispatcher = $dispatcher; 12 } 13 14 public function sendEmailVerificationNotification( 15 User $user, 16 string $url 17 ): void { 18 $this >dispatcher >send( 19
  51. Infra Notifier Infra\Notifier\Illuminate\UserNotifier use Domain\User\User; use Domain\User\UserNotifier as UserNotifierInterface; use

    Illuminate\Auth\Notifications\VerifyEmail; use Infra\Notifiable\User as NotifiableUser; use Infra\Notifications\VerifyEmail; class UserNotifier implements UserNotifierInterface { private Dispatcher $dispatcher; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } public function sendEmailVerificationNotification( User $user, string $url ): void { $this >dispatcher >send( 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class UserNotifier implements UserNotifierInterface use Domain\User\User; 1 use Domain\User\UserNotifier as UserNotifierInterface; 2 use Illuminate\Auth\Notifications\VerifyEmail; 3 use Infra\Notifiable\User as NotifiableUser; 4 use Infra\Notifications\VerifyEmail; 5 6 7 { 8 private Dispatcher $dispatcher; 9 10 public function __construct(Dispatcher $dispatcher) { 11 $this->dispatcher = $dispatcher; 12 } 13 14 public function sendEmailVerificationNotification( 15 User $user, 16 string $url 17 ): void { 18 $this >dispatcher >send( 19 public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } use Illuminate\Auth\Notifications\VerifyEmail; 3 use Infra\Notifiable\User as NotifiableUser; 4 use Infra\Notifications\VerifyEmail; 5 6 class UserNotifier implements UserNotifierInterface 7 { 8 private Dispatcher $dispatcher; 9 10 11 12 13 14 public function sendEmailVerificationNotification( 15 User $user, 16 string $url 17 ): void { 18 $this->dispatcher->send( 19 new NotifiableUser($user), 20 new VerifyEmail($url) 21
  52. Infra Notifier Infra\Notifier\Illuminate\UserNotifier use Domain\User\User; use Domain\User\UserNotifier as UserNotifierInterface; use

    Illuminate\Auth\Notifications\VerifyEmail; use Infra\Notifiable\User as NotifiableUser; use Infra\Notifications\VerifyEmail; class UserNotifier implements UserNotifierInterface { private Dispatcher $dispatcher; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } public function sendEmailVerificationNotification( User $user, string $url ): void { $this >dispatcher >send( 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class UserNotifier implements UserNotifierInterface use Domain\User\User; 1 use Domain\User\UserNotifier as UserNotifierInterface; 2 use Illuminate\Auth\Notifications\VerifyEmail; 3 use Infra\Notifiable\User as NotifiableUser; 4 use Infra\Notifications\VerifyEmail; 5 6 7 { 8 private Dispatcher $dispatcher; 9 10 public function __construct(Dispatcher $dispatcher) { 11 $this->dispatcher = $dispatcher; 12 } 13 14 public function sendEmailVerificationNotification( 15 User $user, 16 string $url 17 ): void { 18 $this >dispatcher >send( 19 public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } use Domain\User\User; 1 use Domain\User\UserNotifier as UserNotifierInterface; 2 use Illuminate\Auth\Notifications\VerifyEmail; 3 use Infra\Notifiable\User as NotifiableUser; 4 use Infra\Notifications\VerifyEmail; 5 6 class UserNotifier implements UserNotifierInterface 7 { 8 private Dispatcher $dispatcher; 9 10 11 12 13 14 public function sendEmailVerificationNotification( 15 User $user, 16 string $url 17 ): void { 18 $this >dispatcher >send( 19 public function sendEmailVerificationNotification( User $user, string $url ): void { $this->dispatcher->send( new NotifiableUser($user), new VerifyEmail($url) ); } 6 class UserNotifier implements UserNotifierInterface 7 { 8 private Dispatcher $dispatcher; 9 10 public function __construct(Dispatcher $dispatcher) { 11 $this->dispatcher = $dispatcher; 12 } 13 14 15 16 17 18 19 20 21 22 23 } 24
  53. Notifiable Infra\Notifiable\User use Domain\User\User as DomainUser; use Illuminate\Notifications\Notifiable; class User

    { use Notifiable; private DomainUser $user; public function __construct(DomainUser $user) { $this->user = $user; } public function routeNotificationForMail() { return $this->user->getRawEmail(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  54. Notifiable Infra\Notifiable\User use Domain\User\User as DomainUser; use Illuminate\Notifications\Notifiable; class User

    { use Notifiable; private DomainUser $user; public function __construct(DomainUser $user) { $this->user = $user; } public function routeNotificationForMail() { return $this->user->getRawEmail(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 use Notifiable; use Domain\User\User as DomainUser; 1 use Illuminate\Notifications\Notifiable; 2 3 class User 4 { 5 6 7 private DomainUser $user; 8 9 public function __construct(DomainUser $user) 10 { 11 $this->user = $user; 12 } 13 14 public function routeNotificationForMail() 15 { 16 return $this->user->getRawEmail(); 17 } 18 } 19
  55. Notification Infra\Notifications\VerifyEmail use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; class VerifyEmail extends IlluminateVerifyEmail

    { private string $verificationUrl; public function __construct(string $verificationUrl) { $this->verificationUrl = $verificationUrl; } /** * @inheritDoc */ public function toMail($notifiable) { return $this->buildMailMessage($this->verificationUrl); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  56. Notification Infra\Notifications\VerifyEmail use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; class VerifyEmail extends IlluminateVerifyEmail

    { private string $verificationUrl; public function __construct(string $verificationUrl) { $this->verificationUrl = $verificationUrl; } /** * @inheritDoc */ public function toMail($notifiable) { return $this->buildMailMessage($this->verificationUrl); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class VerifyEmail extends IlluminateVerifyEmail use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; 1 2 3 { 4 private string $verificationUrl; 5 6 public function __construct(string $verificationUrl) 7 { 8 $this->verificationUrl = $verificationUrl; 9 } 10 11 /** 12 * @inheritDoc 13 */ 14 public function toMail($notifiable) 15 { 16 return $this->buildMailMessage($this->verificationUrl); 17 } 18 } 19
  57. Notification Infra\Notifications\VerifyEmail use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; class VerifyEmail extends IlluminateVerifyEmail

    { private string $verificationUrl; public function __construct(string $verificationUrl) { $this->verificationUrl = $verificationUrl; } /** * @inheritDoc */ public function toMail($notifiable) { return $this->buildMailMessage($this->verificationUrl); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class VerifyEmail extends IlluminateVerifyEmail use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; 1 2 3 { 4 private string $verificationUrl; 5 6 public function __construct(string $verificationUrl) 7 { 8 $this->verificationUrl = $verificationUrl; 9 } 10 11 /** 12 * @inheritDoc 13 */ 14 public function toMail($notifiable) 15 { 16 return $this->buildMailMessage($this->verificationUrl); 17 } 18 } 19 private string $verificationUrl; public function __construct(string $verificationUrl) { $this->verificationUrl = $verificationUrl; } use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; 1 2 class VerifyEmail extends IlluminateVerifyEmail 3 { 4 5 6 7 8 9 10 11 /** 12 * @inheritDoc 13 */ 14 public function toMail($notifiable) 15 { 16 return $this->buildMailMessage($this->verificationUrl); 17 } 18 } 19
  58. Notification Infra\Notifications\VerifyEmail use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; class VerifyEmail extends IlluminateVerifyEmail

    { private string $verificationUrl; public function __construct(string $verificationUrl) { $this->verificationUrl = $verificationUrl; } /** * @inheritDoc */ public function toMail($notifiable) { return $this->buildMailMessage($this->verificationUrl); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class VerifyEmail extends IlluminateVerifyEmail use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; 1 2 3 { 4 private string $verificationUrl; 5 6 public function __construct(string $verificationUrl) 7 { 8 $this->verificationUrl = $verificationUrl; 9 } 10 11 /** 12 * @inheritDoc 13 */ 14 public function toMail($notifiable) 15 { 16 return $this->buildMailMessage($this->verificationUrl); 17 } 18 } 19 private string $verificationUrl; public function __construct(string $verificationUrl) { $this->verificationUrl = $verificationUrl; } use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; 1 2 class VerifyEmail extends IlluminateVerifyEmail 3 { 4 5 6 7 8 9 10 11 /** 12 * @inheritDoc 13 */ 14 public function toMail($notifiable) 15 { 16 return $this->buildMailMessage($this->verificationUrl); 17 } 18 } 19 /** * @inheritDoc */ public function toMail($notifiable) { return $this->buildMailMessage($this->verificationUrl); } use Illuminate\Auth\Notifications\VerifyEmail as IlluminateVerifyEmail; 1 2 class VerifyEmail extends IlluminateVerifyEmail 3 { 4 private string $verificationUrl; 5 6 public function __construct(string $verificationUrl) 7 { 8 $this->verificationUrl = $verificationUrl; 9 } 10 11 12 13 14 15 16 17 18 } 19
  59. 認証 URL の⽣成処理はどこ? Illuminate\Auth\Notifications\VerifyEmail getKey は Eloquent Model のメソッドなので使えない! 認証

    URL の⽣成を Notification から App User に切り出そう use Illuminate\Notifications\Notification; class VerifyEmail extends Notification { // ... /** * 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); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  60. 認証 URL の⽣成処理はどこ? Illuminate\Auth\Notifications\VerifyEmail getKey は Eloquent Model のメソッドなので使えない! 認証

    URL の⽣成を Notification から App User に切り出そう use Illuminate\Notifications\Notification; class VerifyEmail extends Notification { // ... /** * 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); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * 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); } // ... 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // 24
  61. 認証 URL の⽣成処理はどこ? Illuminate\Auth\Notifications\VerifyEmail getKey は Eloquent Model のメソッドなので使えない! 認証

    URL の⽣成を Notification から App User に切り出そう use Illuminate\Notifications\Notification; class VerifyEmail extends Notification { // ... /** * 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); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * 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); } use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $verificationUrl = $this->verificationUrl($notifiable); 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 20 return $this->buildMailMessage($verificationUrl); 21 } 22 23 // ... 24
  62. 認証 URL の⽣成処理はどこ? Illuminate\Auth\Notifications\VerifyEmail getKey は Eloquent Model のメソッドなので使えない! 認証

    URL の⽣成を Notification から App User に切り出そう use Illuminate\Notifications\Notification; class VerifyEmail extends Notification { // ... /** * 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); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * 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); } use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $verificationUrl = $this->verificationUrl($notifiable); use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 // ... /** * 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( 'verification.verify', Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), [ 'id' $ tifi bl t () 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
  63. 認証 URL の⽣成処理はどこ? Illuminate\Auth\Notifications\VerifyEmail getKey は Eloquent Model のメソッドなので使えない! 認証

    URL の⽣成を Notification から App User に切り出そう use Illuminate\Notifications\Notification; class VerifyEmail extends Notification { // ... /** * 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); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * 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); } use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $verificationUrl = $this->verificationUrl($notifiable); use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 $verificationUrl = $this->verificationUrl($notifiable); 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 return URL::temporarySignedRoute( 'verification.verify', Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), [ 'id' => $notifiable->getKey(), 'hash' => sha1($notifiable->getEmailForVerification()), ] ); */ 31 protected function verificationUrl($notifiable) 32 { 33 if (static::$createUrlCallback) { 34 return call_user_func(static::$createUrlCallback, $notifiable); 35 } 36 37 38 39 40 41 42 43 44 45 } 46 47 // ... 48 } 49
  64. 認証 URL の⽣成処理はどこ? Illuminate\Auth\Notifications\VerifyEmail getKey は Eloquent Model のメソッドなので使えない! 認証

    URL の⽣成を Notification から App User に切り出そう use Illuminate\Notifications\Notification; class VerifyEmail extends Notification { // ... /** * 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); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * 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); } use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $verificationUrl = $this->verificationUrl($notifiable); use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 $verificationUrl = $this->verificationUrl($notifiable); 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 $verificationUrl = $this->verificationUrl($notifiable); 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 [ 'id' => $notifiable->getKey(), 'hash' => sha1($notifiable->getEmailForVerification()), ] */ 31 protected function verificationUrl($notifiable) 32 { 33 if (static::$createUrlCallback) { 34 return call_user_func(static::$createUrlCallback, $notifiable); 35 } 36 37 return URL::temporarySignedRoute( 38 'verification.verify', 39 Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), 40 41 42 43 44 ); 45 } 46 47 // ... 48 } 49
  65. 認証 URL の⽣成処理はどこ? Illuminate\Auth\Notifications\VerifyEmail getKey は Eloquent Model のメソッドなので使えない! 認証

    URL の⽣成を Notification から App User に切り出そう use Illuminate\Notifications\Notification; class VerifyEmail extends Notification { // ... /** * 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); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * 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); } use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $verificationUrl = $this->verificationUrl($notifiable); use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 $verificationUrl = $this->verificationUrl($notifiable); 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 $verificationUrl = $this->verificationUrl($notifiable); 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 use Illuminate\Notifications\Notification; 1 2 class VerifyEmail extends Notification 3 { 4 // ... 5 6 /** 7 * Build the mail representation of the notification. 8 * 9 * @param mixed $notifiable 10 * @return \Illuminate\Notifications\Messages\MailMessage 11 */ 12 public function toMail($notifiable) 13 { 14 $verificationUrl = $this->verificationUrl($notifiable); 15 16 if (static::$toMailCallback) { 17 return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl); 18 } 19 'id' => $notifiable->getKey(), */ 31 protected function verificationUrl($notifiable) 32 { 33 if (static::$createUrlCallback) { 34 return call_user_func(static::$createUrlCallback, $notifiable); 35 } 36 37 return URL::temporarySignedRoute( 38 'verification.verify', 39 Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), 40 [ 41 42 'hash' => sha1($notifiable->getEmailForVerification()), 43 ] 44 ); 45 } 46 47 // ... 48 } 49
  66. App User App\Models\User Eloquent Model と Notifiable への依存がなくなった! Respository と

    Notifier のインターフェースにのみ依存 use Domain\User\UserId; use Domain\User\UserNotifier; use Domain\User\UserRepository; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Foundation\Auth\Access\Authorizable; class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Authorizable; private UserId $id; private UserRepository $repository; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  67. App User App\Models\User Eloquent Model と Notifiable への依存がなくなった! Respository と

    Notifier のインターフェースにのみ依存 use Domain\User\UserId; use Domain\User\UserNotifier; use Domain\User\UserRepository; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Foundation\Auth\Access\Authorizable; class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Authorizable; private UserId $id; private UserRepository $repository; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 10 11 12 13 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 private UserNotifier $notifier; 20 21
  68. App User App\Models\User Eloquent Model と Notifiable への依存がなくなった! Respository と

    Notifier のインターフェースにのみ依存 use Domain\User\UserId; use Domain\User\UserNotifier; use Domain\User\UserRepository; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Foundation\Auth\Access\Authorizable; class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Authorizable; private UserId $id; private UserRepository $repository; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 10 11 12 13 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 private UserId $id; private UserRepository $repository; private UserNotifier $notifier; public function __construct( UserId $id, UserRepository $repository, UserNotifier $notifier ) { $this->id = $id; $this->repository = $repository; $this->notifier = $notifier; } { 15 use Authorizable; 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 // ... 32 33
  69. App User App\Models\User Eloquent Model と Notifiable への依存がなくなった! Respository と

    Notifier のインターフェースにのみ依存 use Domain\User\UserId; use Domain\User\UserNotifier; use Domain\User\UserRepository; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Foundation\Auth\Access\Authorizable; class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Authorizable; private UserId $id; private UserRepository $repository; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 10 11 12 13 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 private UserId $id; private UserRepository $repository; use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 18 19 /** * @inheritDoc */ public function markEmailAsVerified() { return $this->repository->markEmailAsVerified($this->id); } $this->repository = $repository; 28 $this->notifier = $notifier; 29 } 30 31 // ... 32 33 34 35 36 37 38 39 40 41 /** 42 * @inheritDoc 43 */ 44 public function sendEmailVerificationNotification() 45 { 46
  70. App User App\Models\User Eloquent Model と Notifiable への依存がなくなった! Respository と

    Notifier のインターフェースにのみ依存 use Domain\User\UserId; use Domain\User\UserNotifier; use Domain\User\UserRepository; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Foundation\Auth\Access\Authorizable; class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Authorizable; private UserId $id; private UserRepository $repository; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 10 11 12 13 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 private UserId $id; private UserRepository $repository; use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 18 19 use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 /** * @inheritDoc */ public function sendEmailVerificationNotification() { $user = $this->repository->get($this->id); $url = $this->createVerificationUrl(); $this->notifier->sendEmailVerificationNotification($user, $url); } public function markEmailAsVerified() 37 { 38 return $this->repository->markEmailAsVerified($this->id); 39 } 40 41 42 43 44 45 46 47 48 49 50 51 52 private function createVerificationUrl(): string 53 { 54 return URL::temporarySignedRoute( 55
  71. App User App\Models\User Eloquent Model と Notifiable への依存がなくなった! Respository と

    Notifier のインターフェースにのみ依存 use Domain\User\UserId; use Domain\User\UserNotifier; use Domain\User\UserRepository; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Foundation\Auth\Access\Authorizable; class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Authorizable; private UserId $id; private UserRepository $repository; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 10 11 12 13 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 private UserId $id; private UserRepository $repository; use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 18 19 use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 private function createVerificationUrl(): string { return URL::temporarySignedRoute( 'verification.verify', Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), [ 'id' => $this->id->rawValue(), 'hash' => sha1($this->getEmailForVerification()), ] ); } { 46 $user = $this->repository->get($this->id); 47 $url = $this->createVerificationUrl(); 48 49 $this->notifier->sendEmailVerificationNotification($user, $url); 50 } 51 52 53 54 55 56 57 58 59 60 61 62 63
  72. App User App\Models\User Eloquent Model と Notifiable への依存がなくなった! Respository と

    Notifier のインターフェースにのみ依存 use Domain\User\UserId; use Domain\User\UserNotifier; use Domain\User\UserRepository; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Foundation\Auth\Access\Authorizable; class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract { use Authorizable; private UserId $id; private UserRepository $repository; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class User implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, MustVerifyEmailContract use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 10 11 12 13 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 private UserId $id; private UserRepository $repository; use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 18 19 use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 use Domain\User\UserId; 1 use Domain\User\UserNotifier; 2 use Domain\User\UserRepository; 3 use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; 4 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 5 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 6 use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; 7 use Illuminate\Foundation\Auth\Access\Authorizable; 8 9 class User implements 10 AuthenticatableContract, 11 AuthorizableContract, 12 CanResetPasswordContract, 13 MustVerifyEmailContract 14 { 15 use Authorizable; 16 17 private UserId $id; 18 private UserRepository $repository; 19 'id' => $this->id->rawValue(), { 46 $user = $this->repository->get($this->id); 47 $url = $this->createVerificationUrl(); 48 49 $this->notifier->sendEmailVerificationNotification($user, $url); 50 } 51 52 private function createVerificationUrl(): string 53 { 54 return URL::temporarySignedRoute( 55 'verification.verify', 56 Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), 57 [ 58 59 'hash' => sha1($this->getEmailForVerification()), 60 ] 61 ); 62 } 63
  73. まとめ メール認証の実装では検討すべき事項が多く、マニュアル実装するのは結構 ⼤変 Laravel は少ない⼿数でメール認証を実装する⼿段を⽤意してくれている 内部実装を理解して依存関係を紐解くことで、保守性・拡張性の⾼いアーキ テクチャに組み替えることができる

  74. None
  75. ありがとうございました!🤲