sanctum: SPA Can not Log out

  • Airlock Version: 1.0
  • Laravel Version: 7.0

Description:

After setting up following the setup here and configuring xsrf and cors, my SPA (in Angular) can log in but then cannot log out.

The docs say that I should use the “standard, session based authentication services that Laravel provides” here so my login runs

Auth::attempt([...]);

and returns 200 or 401 on success or failure respectively.

And as is written here within my logout function I call

Auth::logout();

but I receive

Method Illuminate\Auth\RequestGuard::logout does not exist.

The docs make no mention of modifying config/auth.php to set the api guard there, and so I haven’t, it is still

'api' => [
    'driver' => 'token',
    'provider' => 'users',
    'hash' => false,
],

The token that I can see through the authenticated user is a TransientToken and I cannot delete/revoke it…?

So until the session expires a user is logged in and cannot be logged out… Am I missing something?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 14
  • Comments: 19

Most upvoted comments

You have to explicitly use the web guard to logout. Its due to the way Airlock registers the airlock guard using viaRequest.

I think there’s probably a better way of handling it but for now, doing this works:

Auth::guard('web')->logout();

auth()->logout works for me (SPA auth) as long as I don’t add the api:sanctum middleware to this route. With this middleware active I get the Method Illuminate\Auth\RequestGuard::logout does not exist error.

~~I am also encountering the issue of the user not being logged out even after Auth::guard('web')->logout(); is called.

In my test case I use Airlock::actingAs($user, ['*'], 'web') to explicitly use the web driver and the logout issue remains~~

Found the problem. All guard accesses must explicitly use ‘web’ Take note of the $this->assertGuest('web') line.

<?php

namespace Tests\Feature\Http\Controllers\Auth;

use App\Http\Controllers\Auth\LoginController;
use App\User;
use Illuminate\Http\Response;
use Tests\TestCase;

class LoginControllerTest extends TestCase {

    public function testLogin() {
        $username = $this->faker->unique()->userName;
        $password = $this->faker->password;
        $user = factory(User::class)->create([
            'username' => $username,
            'password' => $password,
        ]);

        $this->assertActionUsesMiddleware(LoginController::class, 'login', 'guest');

        $this->post(route('login'), [
            'username' => $username,
            'password' => 'invalid',
        ], [
            'Accept' => 'application/json',
        ])->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);

        $this->post(route('login'), [
            'username' => $username,
            'password' => $password,
        ])->assertOk();
        $this->assertAuthenticatedAs($user);

        $this->post(route('login'))->assertRedirect();
    }

    public function testLogout() {
        $this->assertActionUsesMiddleware(LoginController::class, 'logout', $this->authMiddleware);

        $this->post(route('logout'))->assertOk();
        $this->assertGuest('web');
    }
}

auth()->logout works for me (SPA auth) as long as I don’t add the api:sanctum middleware to this route. With this middleware active I get the Method Illuminate\Auth\RequestGuard::logout does not exist error.

I was devastated, but removing ‘api:sanctum’ middleware from logout made it work. I think this issue should be solved as soon as possible

This issue is still present in the current sanctum version. I am using Laravel 9 and logging out inside a test is not possible. It’s like the user authenticated with the actingAs-method persists for all requests inside the test.

Yeah this doesn’t actually seem to work anymore. @driesvints: can you see if there was a regression here?

Actually, this could be related to using sanctum in a stateless mobile api context instead of SPA…

I am having this issue that Auth::guard(‘web’)->logout(); isn’t working. I am still authenticated with a SPA. @driesvints please can you advise what could be the reason why the above isn’t invalidating the session cookie?

I have also tried using the built in logout route and that also doesn’t invalidate the session cookie. Again I am still authenticated.

You have to explicitly use the web guard to logout. Its due to the way Airlock registers the airlock guard using viaRequest.

I think there’s probably a better way of handling it but for now, doing this works:

Auth::guard('web')->logout();

Setting this in a guard() method within LoginController works for me.

/**
 * Get the guard to be used during authentication.
 *
 * @return \Illuminate\Contracts\Auth\StatefulGuard
 */
protected function guard()
{
    return Auth::guard('web');
}

It may or may not be related. Thanks to the guard the function now executes but it does look it’s not working. This is my test :

    {
        Airlock::actingAs(
            factory(User::class)->create()
        );

        $this->assertAuthenticated();
        $response = $this->post('/logout');
        $response->assertNoContent();
        $this->assertGuest();
    }

The test fails on the last line because the user still is authenticated This is the logout function used :

    {
        try {
            //log user out
            Auth::guard('web')->logout();

            return response()->json('', 204);
        } catch (\Exception $e) {
            //return error message
            return response()->json('error_logout', 500);
        }
    }

Is it related ? Is there something I’m missing ?