Search code examples
laravelauthenticationlaravel-jetstreamlaravel-fortify

Redirect logged-in User to Profile Page (to Enable 2-factor) in Laravel Jetstream / Fortify?


I've been looking into a way to redirect a logged in user to their Profile Page if the user does not have 2-factor auth enabled for their profile (ideally would like to do this for all routes). I am using Laravel 8, Fortify and Jetstream.

It looks like 2-factor is not enabled if two_factor_secret & two_factor_recovery_codes in the users table are NULL for the User in question. It is at least enabled if those DB columns are not NULL. One place to possibly check looks like in:

/app/Providers/FortifyServiceProvider.php

It by default sets:

$user = User::where('email', $request->email)->first();

verifies the login, and then I do some housekeeping if they are authenticated.

By default, there is a return $user at the end of the script.

I can check to see if $user->two_factor_secret in not null in the script, so if it is possible to change the route on the fly somehow there that might work, at least a far as the initial login is concerned, but I would prefer to check their 2-factor enabled status (just the status, not a code) for every route in the app. It isn't really clear to me how to do that ? In a ServiceProvider somewhere or in the routes/web.php, etc. I guess it is possible to wrap all of my routes in a group somehow, and they check the 2-step status in the routes/web.php somehow before processing the requested route ?

Any help would be appreciated. There are really two options. Just a warning and redirection to the Profile Page (/user/profile) upon login if they do not have 2-step activated (i.e. DB field populated with data), and then the more global option to always redirect to the profile page for any route if they do not have 2-step enabled (that would prevent navigation to any other page than the profile page).

Thanks.


Solution

  • I would recommend creating a custom middleware which can be applied to a group of routes which will determine if the authenticated user has 2FAenabled.

    namespace App\Http\Middleware;
    
    use Closure;
    
    class HasTwoFactorAuthEnabled
    {
        public function handle($request, Closure $next)
        {
            if (is_null(Auth::user()->two_factor_secret)) {
                return redirect(route('user.profile.2fa'));
            }
    
            return $next($request);
        }
    }
    

    In the above, if the value of two_factor_secret for the authenticated user is null, we assume it is not enabled and therefore redirect the user. Otherwise the request continues to the next middleware.

    As you likely don't want to perform this check on all your HTTP requests or even all your web requests, you will want to register the middleware in the $routeMiddleware array in app/Http/Kernel.php.

    protected $routeMiddleware = [
        'has2FaEnabled' => \App\Http\Middleware\HasTwoFactorAuthEnabled::class,
    ];
    

    Then you can apply the middleware to individual routes, or a group of routes. For example;

    Route::group(['middleware' => ['has2FaEnabled']], function () {
        Route::get('/profile', ProfileController::class);
    });
    

    Now, whether or not you want/should redirect users if they have not enabled 2FA is really down to your use-case and the type of application. If security is important to you or the data managed within your application is sensitive then enforcing 2FA might be a good idea. Otherwise if you're just providing 2FA features for additional security, I might recommend avoiding forcing users to enable it and instead simply recommend that users enable it or prevent access to certain 'sensitive' features if it is not enabled.