'How to reset Laravel AuthManager/guards in between API calls in tests?

I'm writing a Feature test for an API and I want to test custom auth logic.

I know that when I call the login API endpoint, Laravel caches the fact that the user is logged in so the next API call in the test would consider the user already authenticated...

So for one test, how do I disable this Laravel magic of auth caching so I can manually provide the Bearer auth token to check if my custom authentication logic works?

I'm thinking something along the lines of clearing the guards in AuthManager, or clearing AuthManager entirely so Laravel would be force to reinitialize it. But I'm having no luck in figuring out how to do that in a way that works.

Here's some pseudo-example code:

public function testLogin()
{
    $responseData = $this->post('/login', ['email' => $this->user->email, 'password' => 'password'])->json();

    $this->get('/endpoint-requiring-auth')->assertOk();

    //
    // $this->logicToResetAuth() ?
    //

    $this->get('/endpoint-requiring-auth')->assertUnauthorized();
    
    // I want to manually have to pass the token to get it to work now.
    $this->get('/endpoint-requiring-auth', ['Authorization' => "Bearer $responseData[access_token]"])
        ->assertOk();
}

Also the unknown reset logic should affect multiple guards.

Oh yeah, I'm writing custom logic around the JWT-Auth library.



Solution 1:[1]

Good question. I was also struggling with this too. Yesterday I performed a deep dive on this subject. Turned out that TokenGuard has en implementation problem as the authenticated user is not refreshed after a new request is loaded. In order to fix this the following is needed:

  1. Extends TokenGuard class into your own class and overload method setRequest. Add '$this->user = null;' before calling the parent implementation by 'return parent::setRequest($request);'
  2. Use Auth::extend to be able to use your custom TokenGuard
  3. After that, every new request resets the authenticated user automatically

See also https://code.tutsplus.com/tutorials/how-to-create-a-custom-authentication-guard-in-laravel--cms-29667

Solution 2:[2]

For jwt-auth, you can do this to clear the auth user:

auth()->forgetGuards();
app()->instance('tymon.jwt', null);

The first line, discards the existing cached guards. And the second, ensures when the guard is re-created, it wont reuse the same underlying jwt instance.

Solution 3:[3]

For Laravel 8.x (and maybe newer)

auth()->forgetGuards();

But for JWT you may need to additionally do:

app('tymon.jwt')->unsetToken();

Or

app()->instance('tymon.jwt', null);

For Laravel 7.x (and maybe older)

As ->forgetGuards() is not invented yet in this version, and guards-array is protected, do something like:

/** @var \Illuminate\Auth\AuthManager $auth */
$auth = app('auth');
$mirror = new \ReflectionObject( $auth );
$prop = $mirror->getProperty( 'guards' );
$prop->setAccessible(true);
$prop->setValue($auth, []);
$prop->setAccessible(false); // Back to protected.

But for JWT, do same as above Laravel 8 section.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mark Baaijens
Solution 2 levi
Solution 3