Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

bobwurtz's avatar

Laravel/Dusk Authentication Issue

I have an issue with invalid Laravel login attempts. Sometimes, using the correct email and password, my app returns that Auth::check() === false. I have this check in a middleware, and if the check fails the user is directed back to the login page. In the past, what I have done to get around this is the following:

if (Auth::check() === false) {
            $request->session()->invalidate();
            $request->session()->regenerateToken();
            return redirect()->route('login');
        }

Invalidating the session and regenerating the token 'fixes' the problem by forcing the user to login again. The second login attempt always works. Annoying - but it was a workaround for something that didn't happen that often so I moved on. I've recently started writing browser tests with Dusk and the issue has come up again. None of my Dusk login attempts are successful - they all fail. Below are two basic tests - the first one fails and the second one passes:

<?php

namespace Tests\Browser;

use App\Models\Cafe;
use App\Models\Role;
use App\Models\User;
use App\Models\UserToCafe;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class LoginTest extends DuskTestCase
{
    /** @test */
    public function a_user_can_login_correctly()
    {
        $roleId = Role::where('name', 'cafe_user')->value('id');

        $user = User::factory()
                        ->state([
                            'role_id' => $roleId,
                        ])
                        ->create();

        $this->browse(function ($browser) use ($user) {
            $browser->loginAs($user->id)
                    ->visit(route('cafe-menu'))
                    ->assertSeeLink('Menu');
        });
    }

    /** @test */
    public function a_user_can_login_correctly_v2()
    {
        $roleId = Role::where('name', 'cafe_user')->value('id');

        $user = User::factory()
                        ->state([
                            'role_id' => $roleId,
                        ])
                        ->create();

        $this->browse(function ($browser) use ($user) {
            $browser->visit('/login')
                    ->type('email', $user->email)
                    ->type('password', 'password')
                    ->click('button[type="submit"]')
                    ->type('email', $user->email)
                    ->type('password', 'password')
                    ->click('button[type="submit"]')
                    ->assertSeeLink('Menu');
        });
    }
}

This authentication issue started happening after I upgraded to Laravel 9. I am now on the latest Laravel release (9.8.1). I experience this login error locally, and in staging and production (I am only using Dusk locally - but the authentication error happens to users on all three sites). Most of my users just stay logged in so it's not huge deal in production, but this issue with testing is very annoying. I could use the "double-login" method in the second test for all future tests but I'd like to solve this issue. Does anyone have any idea about what could be going on here? Thank you!

0 likes
4 replies
fylzero's avatar

@bobwurtz I believe the loginAs function requires the actual user object, not just the id.

Try removing ->id after $user in your loginAs function. Like so: loginAs($user))

bobwurtz's avatar

@fylzero Thank you for the reply. I should've picked that up in the documentation. Unfortunately, this does not solve the problem. Authentication still fails with:

$this->browse(function ($browser) use ($user) {
            $browser->loginAs($user)
                    ->visit(route('cafe-menu'))
                    ->assertSeeLink('Menu');
        });

In the DuskTestCase.php I comment out the --headless option to watch what is going on. The loginAs produces a blank white screen with a custom Dusk URL (this looks like the expected behavior). However, the next step is that I am brought to the login page. This is the same behavior I occasionally experience locally, in staging, and in production. However, it is not constant so I haven't been able to pinpoint what is going on.

Here are the routes:

Route::domain('cafe.'.config('app.domain'))->middleware(['cafe'])->group(function () {
    // show the menu
    Route::get('/contact', [ContactController::class, 'index'])->name('cafe-contact');

    // show the menu
    Route::get('/menu', [MenuController::class, 'index'])->name('cafe-menu');

    // show the cart
    Route::get('/cart', [CartController::class, 'index'])->name('cafe-cart');

    // show orders
    Route::get('/orders', [OrdersController::class, 'index'])->name('cafe-orders');

    // admin portal
    Route::get('/admin', [AdminController::class, 'index'])->name('cafe-admin');

    // profile
    Route::get('/user/cafe/profile', [ProfileController::class, 'index'])->name('cafe-user-profile');
});

And here is the middleware:

<?php

namespace App\Http\Middleware;

use App\Models\UserToCafe;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class CafeCustomer
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        // must be a cafe_user
        if (Auth::check() === false) {
            $request->session()->invalidate();
            $request->session()->regenerateToken();
            return redirect()->route('login');
        }

        if (UserToCafe::where('user_id', Auth::id())->count() === 0) {
            return redirect()->route('welcome');
        }

        // check for password reset force
        if ($request->user()->reset_password == 1 && strpos($request->path(), 'profile') == false) {
            session()->flash('info', 'You must reset your password before continuing.');

            return redirect()->route('cafe-user-profile');
        }

        return $next($request);
    }
}

I use the $request->session()->invalidate(); $request->session()->regenerateToken(); because through trial and error I found that it resets whatever the problem is and the second login attempt is successful after the invalidation and regeneration. That's why the second test succeeds.

Using dd() I can see that the issue with the Dusk loginAs is that Auth::check() === false, so I know that's where the problem is.

Any thoughts about what could be causing this?

TimothePearce's avatar

Hi @bobwurtz, I have the exact same behaviour. I'm sometimes redirected to the login page just after calling the loginAs method.

Did you find a way to solve this issue?

bobwurtz's avatar

@TimothePearce Yes I did. I believe my issue was caused because I was using a subdomain and there was an issue with the browser not generating the proper cookie to include the subdomain. To fix this, I added SESSION_DOMAIN='.my-app-name.test' to my local .env file (notice the . before the app name). Run php artisan optimize to make sure the new value takes affect. This is assuming you haven't changed the default 'domain' => env('SESSION_DOMAIN'), in your session.php config file.

Please or to participate in this conversation.