phplaravelfortifyguard

Laravel - Fortify guard for different routes and unit tests


I am currently developing unit test for my routes and I ran into an weird situation.

I have some routes that I created in the routes/fortify.php file.

Some one those routes use another fortify.guard (admin and user doesn't have access to the same pages).

In order to do that, I wrote this in the FortifyServiceProvider class :

    /**
     * Register any application services.
     */
    public function register(): void
    {
        Fortify::ignoreRoutes();
        if(request()->is('api/v1/admin/*')){
            config()->set('fortify.guard', 'admin');
        }
    }

Now, when I run php artisan test it doesn't work anymore, since the request()'s route is GET / (that, I do not know why.)

See the code of my test below :


    public function loginAsTestAdmin(): void
    {
        $payload = ['email' => 'admin@example.com', 'password' => bcrypt('password')];
        $this->post('api/v1/admin/login', $payload)->assertStatus(200);
    }

When calling this route, the if(request()->is('api/v1/admin/*')) test fail and therefore, the guard attempt to retrieve the model User instead of the model Admin in the database and ends up with authentication failure (since the user with email 'admin@example.com' does not exist).

So, I figured out that dynamically changing the config to set the correct guard is probably not the Laravel way of doing this.

How should I declare my routes so they use the correct guard ?


Solution

  • Perhaps something like this in config/auth.php

    'guards' => [
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
    
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    ],
    

    and in routes/fortify.php:

    Route::middleware(['auth:admin'])->group(function () {
        // routes protected by the admin guard, for example
        Route::post('api/v1/admin/login', [\App\Http\Controllers\AdminLoginController::class, 'login']);
    });
    
    Route::middleware(['auth'])->group(function () {
        // routes protected by the default guard or user guard
    });
    

    This way you don't have to change the guard dynamically, you just specify the guard for each route, or a group of routes.

    This is how you authenticate using the 'admin' guard

    Auth::guard('admin')->attempt($credentials);
    

    This is how you can access the authenticated user so you can use the object or user data:

    $user = Auth::guard('admin')->user();
    

    And don't forget the use:

    use Illuminate\Support\Facades\Auth;