0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel Fortify】新しいスターターキットで二要素認証が動かない...どうやって使うの?いっそ自作したい!!

Last updated at Posted at 2025-12-21

スターターキットを用意するときに Laravel's built-in authentication を選択すると、はじめからユーザー登録機能やログイン機能が用意されます!

$ laravel new <project name>
 ┌ Which authentication provider do you prefer? ────────────────┐
 │ Laravel's built-in authentication                            │
 └──────────────────────────────────────────────────────────────┘

ただこちらの機能、かなり隠匿されているので、どう改造したらよいか分かりにくいと思います...
果たしてログイン機能はどのように作成したらよいか。その視点で機能を説明します。

Fortify の仕組み

スターターキットを起動すると、以下のログイン画面が表示されるかと思います。

image.png

まず、Login 画面ってどのように呼ばれているのよ...
Controller を探しても Page を探しても、ルーティングは書かれていません...

ということで、どのルートが紐づいているかを route:list コマンドで確認してみます。

$ php artisan route:list

  GET|HEAD  / ..................................................................................................................... home
  GET|HEAD  dashboard ........................................................................................................ dashboard
  POST      email/verification-notification ........ verification.send › Laravel\Fortify › EmailVerificationNotificationController@store
  GET|HEAD  email/verify ............................ verification.notice › Laravel\Fortify › EmailVerificationPromptController@__invoke
  GET|HEAD  email/verify/{id}/{hash} ............................ verification.verify › Laravel\Fortify › VerifyEmailController@__invoke
  GET|HEAD  forgot-password .................................... password.request › Laravel\Fortify › PasswordResetLinkController@create
  POST      forgot-password ....................................... password.email › Laravel\Fortify › PasswordResetLinkController@store
  GET|HEAD  login ...................................................... login › Laravel\Fortify › AuthenticatedSessionController@create
  POST      login ................................................. login.store › Laravel\Fortify › AuthenticatedSessionController@store
  POST      logout ................................................... logout › Laravel\Fortify › AuthenticatedSessionController@destroy
  GET|HEAD  register ...................................................... register › Laravel\Fortify › RegisteredUserController@create
  POST      register ................................................. register.store › Laravel\Fortify › RegisteredUserController@store
  POST      reset-password ............................................. password.update › Laravel\Fortify › NewPasswordController@store
  GET|HEAD  reset-password/{token} ..................................... password.reset › Laravel\Fortify › NewPasswordController@create
  ANY       settings ........................................................................... Illuminate\Routing › RedirectController
  GET|HEAD  settings/appearance ........................................................................................ appearance.edit
  GET|HEAD  settings/password .................................................... user-password.edit › Settings\PasswordController@edit
  PUT       settings/password ................................................ user-password.update › Settings\PasswordController@update
  GET|HEAD  settings/profile ............................................................ profile.edit › Settings\ProfileController@edit
  PATCH     settings/profile ........................................................ profile.update › Settings\ProfileController@update
  DELETE    settings/profile ...................................................... profile.destroy › Settings\ProfileController@destroy
  GET|HEAD  settings/two-factor ...................................... two-factor.show › Settings\TwoFactorAuthenticationController@show
  GET|HEAD  storage/{path} ............................................................................................... storage.local
  GET|HEAD  two-factor-challenge ................... two-factor.login › Laravel\Fortify › TwoFactorAuthenticatedSessionController@create
  POST      two-factor-challenge .............. two-factor.login.store › Laravel\Fortify › TwoFactorAuthenticatedSessionController@store
  GET|HEAD  up ......................................................................................................................... 
  GET|HEAD  user/confirm-password .............................. password.confirm › Laravel\Fortify › ConfirmablePasswordController@show
  POST      user/confirm-password ....................... password.confirm.store › Laravel\Fortify › ConfirmablePasswordController@store
  GET|HEAD  user/confirmed-password-status ............ password.confirmation › Laravel\Fortify › ConfirmedPasswordStatusController@show
  POST      user/confirmed-two-factor-authentication two-factor.confirm › Laravel\Fortify › ConfirmedTwoFactorAuthenticationController@…
  POST      user/two-factor-authentication ............... two-factor.enable › Laravel\Fortify › TwoFactorAuthenticationController@store
  DELETE    user/two-factor-authentication ............ two-factor.disable › Laravel\Fortify › TwoFactorAuthenticationController@destroy
  GET|HEAD  user/two-factor-qr-code .............................. two-factor.qr-code › Laravel\Fortify › TwoFactorQrCodeController@show
  GET|HEAD  user/two-factor-recovery-codes .................. two-factor.recovery-codes › Laravel\Fortify › RecoveryCodeController@index
  POST      user/two-factor-recovery-codes ....... two-factor.regenerate-recovery-codes › Laravel\Fortify › RecoveryCodeController@store
  GET|HEAD  user/two-factor-secret-key ..................... two-factor.secret-key › Laravel\Fortify › TwoFactorSecretKeyController@show

ログインはこの中のここ。

  GET|HEAD  login ...................................................... login › Laravel\Fortify › AuthenticatedSessionController@create
  POST      login ................................................. login.store › Laravel\Fortify › AuthenticatedSessionController@store

Laravel\Fortify がデフォルトで入ってるみたいですね。

その中の AuthenticatedSessionController が認証のカギみたいです。

この LoginViewResponseContract なので、インターフェースみたいなやつです。

use Laravel\Fortify\Contracts\LoginViewResponse;

実体はどこにあるかというと...

ありました。 loginView()$view を生成してますね。
この $view はどこから来るかというと、生成されている FortifyServiceProvider で呼ばれていました!

Fortify は各画面を Fortify のインスタンスに差し込むことで、自由にカスタマイズすることができます。
各種ルーティングは、この app/Providers/FortifyServiceProvider.php のファイルで定義されているので、自由にカスタマイズすることが可能です。

また、各画面は resources/js/pages/auth/***.vue に用意されているので、入出力をそのままに改造するといい感じに変更ができます。

Fortify はロジックを全て任せられる、汎用認証ライブラリなんですね。

二段階認証

Fortify は最初から二段階機能が備わっています。

image.png

ただこの二要素認証、ちょっと直さないと動かないです...。

Starter Kit... これでよいのか...?

直し方、動かし方

何がいけないのかというと、二要素認証に利用する two_factor_secrettwo_factor_recovery_codes に Factory でランダムな文字列が生成されているせいです。
本来ここには APP_KEY で暗号化された文字を登録する必要があります。

なので Seeder を流す際に、デフォルト値を null にしておきます。

database/seeders/DatabaseSeeder.php
class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // User::factory(10)->create();

        User::factory()->create([
            'name' => 'Test User',
            'email' => 'test@example.com',
+           'two_factor_secret' => null,
+           'two_factor_recovery_codes' => null,
        ]);
    }
}
$ php artisan migrate:fresh --seed

すると、二要素認証の画面をスキップして、ダッシュボードが表示されます。

ダッシュボード左下の名前ボタンから「Settings > Tow-Factor Auth」 を選んでください。

image.png

こちらの「Enable 2FA」 ボタンを押すと、専用の QR コードが表示されます。
これを二要素認証用のアプリに登録してください。

有名なのだと Google 認証システム が良いかと思います。

登録すると、二要素認証用のコードが表示されます。

このコードをアプリに入力することで、二要素認証が利用できるようになります!

手動で実装しようとするととても大変なのですが、こんな機能もデフォルトで用意されているんですねぇ:relaxed:

消し方

逆に、この二要素認証邪魔!!!というときもあると思います。
そんな時は Fortify の設定から利用する機能を選択することができます。

以下のように features から twoFactorAuthentication() を削除してください。
これだけで、機能を OFF にすることが可能です!

ほかにも登録画面が邪魔なら Features::registration() を削除するだけど、とても簡単に切り替えられます。

config/fortify.php
    'features' => [
        Features::registration(),
        Features::resetPasswords(),
        Features::emailVerification(),
-       Features::twoFactorAuthentication([
-           'confirm' => true,
-           'confirmPassword' => true,
-           // 'window' => 0
-       ]),
    ],

他にも認証に利用している User モデルから二要素認証の機能を削除することでも削除できます。
モデル自体がその認証方法に対応していないと、二要素認証は機能しないようになっています。

app/Models/User.php
class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
-   use HasFactory, Notifiable, TwoFactorAuthenticatable;
+   use HasFactory, Notifiable;

いやそんなことはいいから、一から作らせてくれ。

っていう需要も非常に大きいと思います。
Fortify はあくまでも最速でログイン機能を用意するもののため、これを使わなければならない!というものではありません。

Laravel はとても簡単に認証機能を作成することができます。

上記リンクからテンプレートをコピペして解説します。

ログイン

以下の処理を POST メソッドなどで呼び出すと実装できます。

    public function authenticate(Request $request): RedirectResponse
    {
        // (1) 入力値のバリデーションを行う
        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        // (2) アカウントの確認と、成功した場合ログインする
        if (Auth::attempt($credentials)) {
            // (2-A) セッションを再生成する
            $request->session()->regenerate();

            // (2-B) ログイン前に見ていたページか dashboard へ遷移する
            return redirect()->intended('dashboard');
        }

        // (3) 認証エラー返却
        return back()->withErrors([
            'email' => 'The provided credentials do not match our records.',
        ])->onlyInput('email');
    }

Auth::attempt($credentials)$credentials に入った値を AND 条件としてユーザーを検索します。
password は必須で、他の要素は任意です。
なので name, password というバリデーション結果を引数に渡すと、email ではなく name で検索することができます。

もしこのチェック機能すら手動で実装したい場合は Auth::login($user); を使いましょう。
ユーザーインスタンスを渡すだけで、その人としてログインすることができます。

ログアウト

以下の処理を POST メソッドなどで呼び出すと実装できます。
セッションを削除する、という意なので DELETE でも面白いかもしれませんね。

public function logout(Request $request): RedirectResponse
{
    // (1) ログアウトする
    Auth::logout();

    // (2) セッションを破棄する
    $request->session()->invalidate();

    // (3) CSRFトークンを再生成する
    $request->session()->regenerateToken();

    // (4) / ページへ移動する
    return redirect('/');
}

Auth ファサード

上記でいろいろと使用している Auth:: が認証の本体です。
Fortify でもこの Auth:: をこねくり回しています。

Illuminate\Support\Facades\Auth

以下のようなコマンドが利用できます。

# 手動ログイン
> Auth::login(User::find(1));
= null

# ログインしているかを確認する
> Auth::check();
= true

# ログイン中のIDを取得する
> Auth::id();               
= 1

# ログイン中のユーザーを取得する
> Auth::user();
= App\Models\User {#6235
    id: 1,
    name: "Test User",
    email: "test@example.com",
    email_verified_at: "2025-12-21 06:49:33",
    #password: "$2y$12$KjLge/FG80ahF1LPaDXgxOwlr6PfUzFV.AEzbM6KKjz.IcMqYvCA.",
    #remember_token: "qD6L9YnDm6",
    created_at: "2025-12-21 06:49:34",
    updated_at: "2025-12-21 06:49:34",
    #two_factor_secret: "EKozr8VITX",
    #two_factor_recovery_codes: "Q6zTiI9ZUj",
    two_factor_confirmed_at: "2025-12-21 06:49:34",
  }

Auth Middleware

これらの認証は Route::middleware('auth') でくくった Route に自動適用されます。
内部で登録した Route は、未認証の場合、自動的にログイン画面に遷移します。

とても便利ですね...。

routes/web.php
Route::middleware('auth')->group(function () {
    Route::get('dashboard', function () {
        return Inertia::render('Dashboard');
    })->name('dashboard');
});

おわりに

こんな感じで、なんでもござれという Laravel の認証

もし特定のサービスを利用した OAuth が行いたい場合は Socialite を利用できます。
Google から Slack、Microsoft 365 などなんでもござれです。

こちらも、もし手動実装したい場合は Passport を導入してください。

おわりに、認証ロジックはとても慎重に実装する必要があるので、既存のものを利用して安全なシステムを構築しましょう!!!パスワードを平文で登録するなんてもってのほかです!!

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?