Laravel9のLaravel Breeze+Reactでマルチ認証(Multi Authentification)

Laravel8のバージョンで公開済みのLaravel Breezeのマルチ認証についてLaravel9でも同様に設定ができるのかを確認した文書です。
公開済みの文書ではLaravel Breezeのデフォルトの場合とInertia+Vueを利用した2つのパターンについて説明を行っていますが今回はInertia+Reactを利用しています。基本的な設定方法はLaravel8とLaravel9で同じなのでデフォルトまたはInertia + Vueの場合は上記の文書を参考にしてください。
本文書を読み進めるだけであればReactの知識は必要ありません。
目次
Laravelプロジェクトの作成
Laravelプロジェクトの作成はcomposerまたはlaravel newコマンドを利用して行います。本文書ではlaravel_breeze_multi_auth_reactというプロジェクト名をつけていますが任意の名前をつけてください。
composerコマンドでもプロジェクトの作成を行うことができます。
作成したプロジェクトに移動してphp artisanコマンドでLaravelのバージョンを確認します。本文書では9.6.0バージョンで動作確認を行っています。
Laravel Breezeを利用するためにはcomposerコマンドを利用してパッケージのインストールを行う必要があります。
パッケージのインストール後にbreezeのインストールを行います。インストール時にデフォルト(Alpine.js)、Inertia.js + Vue.js, Inertia.js + Reactのどれを利用するのかオプションで指定することができます。Reactを利用する場合はreactを指定します。
php artisan breeze:installを実行すると認証に関連するルーティングの追加、ユーザ登録、ログイン処理が記述されたコントローラーファイル、ユーザ登録、ログイン画面に関するファイルが作成されます。
breezeのインストールが完了したらnpm install && npm run devコマンドを実行してJavaScriptのパッケージとビルドを行います。
ビルドが完了後にphp artisan serveコマンドでLaravelの開発サーバの起動を行います。Breezeのインストールが完了しているので右上にはLogin inとRegisterのリンクが表示されます。各リンクをクリックするとログイン画面とユーザ登録画面が表示されます。

データベースの設定
Laravelプロジェクトの作成とBreezeのインストールが完了するとユーザの登録画面が表示されますがデータベースがないためユーザの登録を行うことができません。ユーザの登録が行えるようにデータベースの作成を行います。簡易的に利用することができるSQLiteデータベースを利用します。SQLiteではデータベースにファイルを利用するためtouchコマンドでdatabaseフォルダの下にdatabase.sqliteファイルを作成します。
LaravelからSQLiteデータベースに接続するために環境変数を含む.envファイルの更新します。.envファイルには先頭にDB_のついている環境変数が複数ありますが、DB_CONNECTIONを残してDB_から始まる変数を削除します。DB_CONNECTIONにはデフォルトでmysqlが設定されていますがsqliteに変更します。
.envファイルを更新後、データベースにテーブルを作成するためにphp artisan migrateコマンドを実行します。
マルチ認証の設定
モデルファイルの作成
マルチ認証を利用する場合は認証によってユーザ情報を保存するテーブルを分けます。デフォルトの認証ではusersテーブルを利用します。新たに認証に利用するテーブルを作成しますがテーブル名についてはマルチ認証を利用するアプリケーションによって変更することができます。本文書ではadminsテーブルを利用するためモデルファイルにはAdmin.phpを利用します。モデルファイルの作成にはphp artisan make:modelコマンドを利用し-mオプションをつけます。-mオプションでマイグレーションファイルも同時に作成されます。
作成されたAdmin.phpファイルはapp¥MoldesフォルダにあるUser.phpの中身をコピーして貼り付けます。コピー後にクラス名はAdminにして保存してください。
作成されたマイグレーションファイルもusersテーブル作成用のマイグレーションファイルの列情報をコピーして利用します。
php artisan make:migrateコマンドを実行してadminsテーブルを作成します。
Guardの設定
Laravelは認証の仕組みとしてGuardを利用します。名前の通りLaravelにアクセスするリクエストに対してガードする(守る)役目を持っています。 デフォルトではブラウザからのアクセス用のwebとAPI用のapiが設定されています。ルーティングに対してweb guardを設定している場合はログイン画面からのユーザの認証が行われていないとweb guardを設定した場所にアクセスすることができません。
Guardは追加を行うことができるので追加認証機能のために新たにadmin guardの追加を行います。設定はconfig/auth.phpファイルで行います。Guardがどのような処理を行われているかがわからなくても追加したadminの内容を見るとモデルApp\Models\Admin 、パスワード、adminsテーブルに関連していることがわかります。
ルーティングの設定
breeze:installを実行するとルーティングのweb.phpファイルにはログイン後に表示されるダッシュボードに関するルーティングと認証に関するルーティングが追加されます。認証に関するルーティングについてはauth.phpファイルに記述されています。
auth.phpファイルによってどのようなルーティングが追加されているかはphp artisan route:listコマンドを実行することがで確認することができます。/login, /registerなど認証に関するルーティングが追加されていることを確認してください。

追加するadmin認証についてのルーティングを追加するためにroutes/auth.phpファイルを複製して/routes/admin.phpファイルを作成します。
作成したadmin.phpでは名前空間の設定変更とmiddlewareにパラメータadminを追加します。
この後認証に関連するコントローラーファイルをapp\HTTP\Controllersの下のAdminの下に保存するのでインポートするクラスのパスを下記のように変更します。Authの前にAdminを追加します。
作成したadmin.phpファイルをauth.phpファイルと同じ方法でweb.phpファイルに追加します。
php artisan route:listコマンドを実行しても追加したルーティング(admin.php)に関するコントローラーが存在しないためエラーになります。
コントローラーの作成
追加のadmin認証に関連するコントローラーファイルを保存するためにapp¥HTTP¥ControllersにAdminフォルダを作成します。Adminフォルダの下にbreeze:install実行後に作成されたapp¥HTTP¥Controllers¥Authフォルダをそのままコピーします。
Admin¥Authフォルダの下には8つのコントロールファイルがありますがすべてのファイルを開いて名前空間の変更を行います。Authの名前の前にAdminを挿入してください。変更の目的はファイルをAdminフォルダにコピーして名前空間のパスが変わったためです。
dmin¥Authフォルダの下にある8つのファイルの名前空間を変更すると先ほど失敗したphp artisan route:listコマンドを実行することができます。
しかし、ルーティングは追加されておらずファイル名の先頭にAdminが追加されているだけです。理由はroutes/auth.phpとadmin.phpファイルのルーティング先の設定が同じためです。auth.phpとadmin.phpでルーティング先の/registerが2回登録され後から追加したadmin.phpファイルのルーティングによってauth.phpファイルのルーティングが上書きされています。上書きされるのは/registerだけでなく/loginなどauth.phpに記述されているすべてのルーティングです。

ルーティングの重複を避けるためデフォルト認証のルーティングの/registerを追加するadmin認証では/admin/registerとします。そのためにadmin.phpファイルのルーティングにprefixで一括で/adminを追加します。URLだけではなくnameも区別するためにname列にnameメソッドによりはadmin.を先頭につけます。
設定後にphp artisan route:listを実行するとURI列にadmin/、Name列にadmin.が追加されたルーティングが表示されます。

ここまでの設定が完了するとブラウザで/admin/register, /admin/loginにアクセスするとユーザ登録画面とログイン画面が表示され/admin/register画面からユーザを登録することができます。しかし、登録したユーザが保存されるテーブルはadminsではなくusersなのでマルチ認証の設定は完了していません。
ユーザ登録、ログイン画面の設定
追加したadmin認証でユーザを正しく登録するためにはフロントエンド画面の設定を変更する必要があります。
Inertia+Reactの場合
admin.phpで追加したルーティングを確認するとユーザ登録画面の/admin/registerにアクセスするとapp¥Http¥Controller¥Admin¥Auth¥RegisteredUserController.phpファイルのcreateメソッドが実行されます。
render関数の中にあるAuth/Registerは¥resources¥js¥Pagesの下に保存されているAuth¥Register.jsファイルです。Register.jsファイルを開いてsubmit関数を確認してください。postリクエストの送信先がregisterになっていることがわかります。
これはデフォルト認証が利用するルーティングなのでregisterではなくadmin.registerに変更する必要があります。Auth¥Register.jsファイルはデフォルトの認証で利用するため更新することはできません。そのためPagesフォルダに新たにAdminフォルダを作成しAuthフォルダをAdminフォルダの下にコピーします。
Admin¥Authの下にコピーしたRegister.jsファイルを開いてsubmitメソッドを変更します。
ログインページへのリンクが設定されているroute関数の引数に設定されている値も変更を行います。loginからadmin.loginに変更しています。loginからadmin.logingへの変更はルーティングのname列の値に対応し/registerから/admin/registerに変更したことを意味します。
Admin¥Auth¥Login.jsファイルのPOSTリエクストの送信先とroute関数の引数を変更します。
その他のConfirmPassword.js, ForgotPassword.js, ResetPasswod.js, VerifyEmail.jsファイルも同様にPOSTリクエスト先とroute関数の設定がある場合は引数の値を変更してください。
jsファイルを更新したら更新内容を反映させるためにビルドが必要になります。npm run watchコマンドを実行するとjsファイルの更新を検知しビルドを自動で行います。
jsファイルの変更が完了したら再度RegisteredUserController.phpファイルを確認します。createメソッドのrenderメソッドはAuth/Registerになっていますが先ほどAdmin/Auth/Registerを作成したので変更を行います。
/admin/registerにブラウザからアクセスするとAdmin/Auth/Registerファイルの内容が表示されることになります。 Admin¥Authフォルダに保存されているコントロラーファイルすべてを確認してInertia::render関数の値を変更します。
ログイン処理に関係するAuthenticatedSessionController.phpでもcreateメソッドのrender関数の値をAuth/LoginからAdmin/Auth/Loginに変更します。
その他のConfirmablePasswordController.php, EmailVerificationPromptController.php, NewPasswordController.php, PasswordResetLinkController.phpファイルでもrender関数の値を更新します。
ユーザの登録
ユーザ登録画面の表示では、Admin¥RegisteredUserController.phpファイルのcreateメソッドに注目していましたがユーザの登録を行う際はstoreメソッドに注目します。
storeメソッドはUserモデルが利用されているためUserからAdminモデルへ変更を行います。またemailのバリデーションでusersテーブルにunique設定が行われているのでusersからadminsに変更します。
ファイルの更新が完了したらブラウザで/admin/registerにアクセスします。ファイルが更新されているか確認するために”Already registgered”にカーソルを当ててリンク先が/admin/loginになっているか確認してください。もし/loginのままの場合は正しいファイルが更新されていないかReactの場合はJavaScriptファイルのキャッシュがクリアされていない可能性があるのでブラウザのキャッシュのクリアを行ってください。

adminsテーブルにユーザが登録できるかどうか確認するために一度データベースをリフレッシュします。
ユーザ登録画面からユーザの登録を行いadminsテーブルにユーザ情報が追加されるか確認を行ってください。本文書ではデータベースのGUIの管理ソフトウェアであるTablePlusを利用していています。

/admin/registerのユーザ登録画面からユーザを登録するとadminsテーブルへのユーザ登録が完了してもここまでの設定では登録後にリダイレクト(/loginへ)が行われブラウザ上にはログイン画面が表示されダッシュボードは表示されません。
ダッシュボードへのリダイレクト
RegisteredUserController.phpでのユーザ登録後の処理を確認します。redirect関数の引数にRouteServiceProvider.phpで設定されているHOMEが指定されています。これはユーザ登録後にHOMEに指定されているURLにリダイレクトされることになります。
RegisteredUserController.phpを開いてHOMEの値を確認します。HOMEには/dashboardが設定されています。
HOMEには/dashboardが設定さているためユーザの作成が完了すると/dashboardにリダイレクトされます。/dashboardはデフォルトのユーザ用のダッシュボードなので新たにadmin認証用のダッシュボードを設定する必要があります。

RouteServiceProvider.phpファイルにADMIN_HOMEを追加します。ADMIN_HOMEには/admin/dashboardを設定します。
ADMIN_HOMEを追加したらRegisteredUserController.phpのリダイレクト先をHOMEからADMIN_HOMEに変更します。これでユーザの登録が完了したらADMIN_HOMEの/admin/dashboardにリダレクトすることになります。
ダッシューボードページの作成
web.phpファイルには/admin/dashboardのルーティングが存在しないので/dashboardを参考に新たに追加します。render関数で指定されているDashboardファイルもadmin用にAdmin/Dashboardに変更しています。middlewareのauthにもパラメータのadminを追加しています。
render関数で指定したAdmin¥Dashboardを作成します。PagesフォルダにあるDashboard.jsをPages¥Adminフォルダにコピーします。
Dashboard.jsファイルの中ではページのレイアウトとしてAuthenticatedコンポーネントを利用しています。Authenticatedコンポーネント内のリンク先はすべてデフォルト認証用のルーティングが設定されているのでresources¥js¥Layoutsフォルダ下にあるAuthenticated.jsファイルをコピーしてAdminAuthenticated.jsファイルを作成します。
作成したAdminAuthenticated.jsファイルではリンク先にadmin用のルーティングを指定するためroute関数の中に設定している値にadmin.をつけています。特にログアウトのroute関数は忘れずに変更を行ってください。
レスポンシブ用のrouteの変更も行います。
AdminAuthenticated.jsファイルの更新が完了したらPages¥Admin¥Dashboard.vueファイルでレイアウトのコンポーネントであるAuthenticatedをAdminAuthenticatedに変更します。
デフォルトとadmin認証用のどちらのDashboard.jsファイルが表示されているのかすぐに区別できるように”You’re logged in!”を”You’re Admin User!”に変更します。
リダイレクトの確認
ユーザ登録後のリダイレクト
追加したadmin認証のダッシュボードページが作成できたら/admin/registerからユーザの登録を行います。ユーザを登録する前にデータベースのリフレッシュを行っておきます。
ブラウザで/admin/registerにアクセスしてユーザの登録を行います。しかし先ほどと変わらずログイン画面が表示されます。
再度ユーザ登録後の処理を確認するためRegisteredUserController.phpファイルを確認します。ユーザ作成後にlogin処理を行う部分でguardにadminを設定します。
再度データベースのリフレッシュを行います。
/admin/registerからユーザを登録するとようやくAdmin用のダッシュボードが表示されます。

ログイン後のリダイレクト
admin認証でログインした状態で/admin/loginや/admin/registerにアクセスを行うと/login画面にリダイレクトされます。ログイン中の場合は/admin/dashboardへリダイレクトされるようにmiddewareのguestに対応するRedirectIfAuthenticated.phpファイルの設定を行います。$guardの値がadminの場合はRouteServiceProviderのADMIN_HOMEにリダイレクトする設定を行っています。
設定後にログインした状態で/admin/registerにアクセスすろ/admin/dashboardにリダイレクトされることが確認できます。

ログアウト処理
ログインしたユーザをログアウトする場合はAuthenticatedSessionController.phpファイルのdestoryメソッドが実行されます。Admin認証なのでAdmin/AuthフォルダにあるAuthenticatedSessionController.phpファイルを更新します。
デフォルトではguardのwebが設定されているのでadminに変更します。リダイレクト先が”/”に設定さているので/admin/loginに変更を行っています。リダイレクト先は任意なので好きな場所を指定してください。
ダッシュボード画面の右側にあるユーザ名をクリックするとドロップダウンメニューが表示されLogoutがあるのでクリックします。クリックすると/admin/login画面にリダイレクトされます。
ログイン処理
ユーザの登録までの設定が完了できたので/admin/login画面からログインを行います。登録済みユーザでログインしようとしてもログインを行うことができません。

ログイン処理を行うHttp¥Controllers¥Admin¥Auth¥AuthenticatedSessionController.phpファイルのstoreメソッドを確認します。authenticateメソッド中で認証のチェックが行われているのでstore関数の引数に設定されているLoginRequeset.phpファイルの中身を確認しますがその前にログイン後のリダイレクト先の場所をHOMEからADMIN_HOMEに変更しています。
LoginRequest.phpファイルの中ではauthenticateで認証のチェック(Auth::attempt)を行っていますがデフォルトではguardを指定しないためデフォルトのwebが利用されます。webを利用した場合はusersテーブルでemailとパスワードをチェックするためadminsテーブルに登録したユーザでは認証に失敗します。
リクエストの変数$requestにはアクセスしてきたURLの情報を持っているのでここでもその情報を利用します。URLがadmin/*の場合は$guardの値をadminにし、それ以外の場合はwebにしています。
設定後、/admin/loginからユーザ登録したユーザでログインを行うとadmin用のダッシュボードが表示されます。
ログイン前のリダイレクト
ログインする前に/admin/dashboardにアクセスを行うと/loginにリダイレクトされます。アクセス制限のある/admin/以下のページにアクセスした場合はadmin認証のログイン画面である/admin/loginにリダイレクトできるように設定を行います。
web.phpファイルで追加したルーティングへアクセスの制限はmiddlewareのauthによって制御されているのでAuthenticate.phpファイルを更新します。認証が行われていない場合はredirectToメソッドが実行されます。デフォルトでは/loginにリダイレクトされるように設定されているのでadmin以下のURLからアクセスの場合はadmin.login(=/admin/login)にリダイレクトするように変更を行います。リクエストの変数$requestにはアクセスしたURLの情報を持っているのでisメソッドを利用することでadmin/*と一致するパスの場合はtrueが戻されます。*はワイルドカードとして利用できます。
Authenticate.phpを更新してログイン前に/admin/dashboardにアクセスすると/admin/loginにリダイレクトされるようになります。
まとめ
ここまでの設定で追加したadmin認証でもユーザの登録、ユーザのログイン、ログアウト処理がデフォルトの認証とは独立して動作するようになりました。パスワードリセットなどの機能を利用する場合は追加の設定が必要となります。