71

I'm currently using 2 projects. 1 front end (with laravel backend to communicate with API) and another laravel project (the API).

Now I use Laravel Passport to authenticate users and to make sure every API call is an authorized call.

Now when I want to log out my user, I send a post request to my API (with Bearer token) and try to log him out of the API (and clear session, cookies,...)

Then on the client I also refresh my session so the token is no longer known. Now when I go back to the login page, it automatically logs in my user. (Or my user is just still logged in).

Can someone explain me how to properly log out a user with Laravel passport?

starball
  • 20,030
  • 7
  • 43
  • 238
Joren vh
  • 770
  • 1
  • 6
  • 12
  • can you see any token in http headers after logout ? – Kalidass May 08 '17 at 10:04
  • I have written an article about this issue: https://emciyevabdulla.medium.com/laravel-passport-logout-auth-user-from-all-devices-super-easy-way-d0638d8a6011 – WebMan Jan 09 '22 at 08:16

12 Answers12

84

Make sure that in User model, you have this imported

use Laravel\Passport\HasApiTokens;

and you're using the trait HasApiTokens in the User model class using

use HasApiTokens

inside the user class. Now you create the log out route and in the controller, do this

$user = Auth::user()->token();
$user->revoke();
return 'logged out'; // modify as per your need

This will log the user out from the current device where he requested to log out. If you want to log out from all the devices where he's logged in. Then do this instead

$tokens =  $user->tokens->pluck('id');
Token::whereIn('id', $tokens)
    ->update(['revoked'=> true]);

RefreshToken::whereIn('access_token_id', $tokens)->update(['revoked' => true]);

Make sure to import these two at the top

use Laravel\Passport\RefreshToken;
use Laravel\Passport\Token;

This will revoke all the access and refresh tokens issued to that user. This will log the user out from everywhere. This really comes into help when the user changes his password using reset password or forget password option and you have to log the user out from everywhere.

Koushik Das
  • 9,678
  • 3
  • 51
  • 50
  • 5
    Tip: In the route file don't forget to put your route inside the group(['middleware' => 'auth:api'] – Fellipe Sanches May 07 '20 at 20:33
  • Thank you! why not just delete all user tokens? – Ahmad Mobaraki Jul 01 '20 at 06:18
  • Depends on the user case. I'm logged in from both my phone and pc, let's say. I just log out from my phone. It'd be a bad user experience to log me out from my pc too if I don't want to log myself out. – Koushik Das Jul 01 '20 at 15:13
  • 1
    `Token::whereIn('id', $tokens)->update(['revoked', true]);` Should have a `=>` in the update ie `Token::whereIn('id', $tokens)->update(['revoked' => true]);` – tbone1000 May 23 '22 at 14:12
54

You need to delete the token from the database table oauth_access_tokens you can do that by creating a new model like OauthAccessToken

  1. Run the command php artisan make:model OauthAccessToken to create the model.

  2. Then create a relation between the User model and the new created OauthAccessToken Model , in User.php add :

    public function AauthAcessToken(){
        return $this->hasMany('\App\OauthAccessToken');
    }
    
  3. in UserController.php , create a new function for logout:

    public function logoutApi()
    { 
        if (Auth::check()) {
           Auth::user()->AauthAcessToken()->delete();
        }
    }
    
  4. In api.php router , create new route :

     Route::post('logout','UserController@logoutApi');
    
  5. Now you can logout by calling posting to URL /api/logout
Tumaini Mosha
  • 83
  • 3
  • 7
Mahdi
  • 835
  • 8
  • 6
  • 1
    What to do about the refresh token? – QuietSeditionist Dec 06 '17 at 15:41
  • 8
    this will delete all entries from AauthAcessToken table against user. What if we want to log out from a specific device only -to delete single entry against user) – Hari Mar 31 '18 at 06:26
  • Since I am new to this, how does laravel creates relationship with the oauth_access_tokens table since it doesn't have any common field with users table – d_bhatnagar May 07 '18 at 05:38
  • this will logout the user from all devices. – Mahmoud Fayez Nov 01 '18 at 12:41
  • 12
    i am using passport 7.4 and ```auth()->user()->token()->revoke();``` just works fine – ManojKiran A Aug 24 '19 at 06:57
  • @QuietSeditionist For anyone who bumps on this, you can create a relationship between the oauth_access_tokens and the oath_refresh_tokens and add an OnDelete Cascade constraint to delete the refresh token whenever the access token is deleted. – dkum Jul 15 '20 at 23:00
  • `Auth::check()` and `Auth::user()` are null in the controller. Likely because `auth:api` middleware hasn't loaded before that. Any ideas how to check if the user is authenticated? Here is my api config: `Route::prefix('/user')->group(function() { Route::post('/login', [UserController::class, 'login']); Route::post('/register', [UserController::class, 'register']); Route::get('/logout', [UserController::class, 'logout']); });` – askepott Nov 13 '20 at 15:11
36

This is sample code i'm used for log out

public function logout(Request $request)
{
    $request->user()->token()->revoke();
    return response()->json([
        'message' => 'Successfully logged out'
    ]);
}
Ramadhan
  • 824
  • 8
  • 10
26

Create a route for logout:

$router->group(['middleware' => 'auth:api'], function () use ($router) {
    Route::get('me/logout', 'UserController@logout');
});

Create a logout function in userController ( or as mentioned in your route)

public function logout() {
        $accessToken = Auth::user()->token();
        DB::table('oauth_refresh_tokens')
            ->where('access_token_id', $accessToken->id)
            ->update([
                'revoked' => true
            ]);

        $accessToken->revoke();
        return response()->json(null, 204);
    }
PHP Worm...
  • 4,109
  • 1
  • 25
  • 48
4

I am using Laravel 6.12.0, below function is working for me.

public function logout(Request $request){
            $accessToken = Auth::user()->token();
            $token= $request->user()->tokens->find($accessToken);
            $token->revoke();
            $response=array();
            $response['status']=1;
            $response['statuscode']=200;
            $response['msg']="Successfully logout";
            return response()->json($response)->header('Content-Type', 'application/json');
        }
3

This is my first post.. and i find a clean solution (Laravel last Version)

/**
 * Logout api
 *
 * @return \Illuminate\Http\Response
 */
public function logout(Request $request)
{        
    if (Auth::check()) {
        $token = Auth::user()->token();
        $token->revoke();
        return $this->sendResponse(null, 'User is logout');
    } 
    else{ 
        return $this->sendError('Unauthorised.', ['error'=>'Unauthorised'] , Response::HTTP_UNAUTHORIZED);
    } 
}
efica
  • 31
  • 3
3

Below is the simplest way I found to do it.

1. USE database SESSION INSTEAD OF file SESSION

Official documention

php artisan session:table
php artisan migrate

Replace SESSION_DRIVER=file by SESSION_DRIVER=database in your .env file.

2. DELETE USER SESSION RIGHT AFTER LOGIN

After a user is redirected to your frontend and logs in to finally get a token, you probably call a route in api/routes.php to get the user information, that's where I'm closing the user backend session before sending back user information to the frontend:

Route::middleware('auth:api')->get('/user', function (Request $request) {
    // Close user session here
    Illuminate\Support\Facades\DB::table('sessions')
        ->whereUserId($request->user()->id)
        ->delete();
    return $request->user();
});

3. REVOKE TOKENS AT LOGOUT

Then, to "log out" (actually, revoke tokens) the user from the frontend, you just need to call another route to revoke the token and refresh_token:

Route::middleware('auth:api')->post('/logout', function (Request $request) {
    // Revoke access token
    // => Set oauth_access_tokens.revoked to TRUE (t)
    $request->user()->token()->revoke();

    // Revoke all of the token's refresh tokens
    // => Set oauth_refresh_tokens.revoked to TRUE (t)
    $refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');
    $refreshTokenRepository->revokeRefreshTokensByAccessTokenId($request->user()->token()->id);

    return;
});

You may prefer to put these two closures in the UserController.

DevonDahon
  • 7,460
  • 6
  • 69
  • 114
0

Hope help someone:

 if (Auth::check()) {
   $request->user()->tokens->each(function ($token, $key) {
        $token->delete();
    });
 }

Good Luck.

vahid sabet
  • 485
  • 1
  • 6
  • 16
0

I use this in my project to logout from multiple device.

public function logout(Request $request, $devices = FALSE)
{
    $this->logoutMultiple(\Auth::user(), $devices);
    return response()->json([], 204);
}

private function logoutMultiple(\App\Models\User $user, $devices = FALSE)
{

    $accessTokens = $user->tokens();

    if ($devices == 'all') {
        
    } else if ($devices == 'other') {
        $accessTokens->where('id', '!=', $user->token()->id);
    } else {
        $accessTokens->where('id', '=', $user->token()->id);
    }

    $accessTokens = $accessTokens->get();

    foreach ($accessTokens as $accessToken) {

        $refreshToken = \DB::table('oauth_refresh_tokens')
            ->where('access_token_id', $accessToken->id)
            ->update(['revoked' => TRUE]);

        $accessToken->revoke();
    }
}
Anil
  • 41
  • 3
0

Try this code to help you to logout from passport authentication.

Route::post('/logout', function(){

        if (Auth::check()) {
            Auth::user()->AauthAcessToken()->delete();
        }

        return response()->json([

            'status'    => 1,
            'message'   => 'User Logout',

        ], 200);
    });

check whether your model contains OauthAccessToken which needs to connect with the database oauth_access_tokens. The access token is stored in the database table oauth_access_tokens. and makes a relation from users to oauth_access_tokens.

public function AauthAcessToken(){
    return $this->hasMany(OauthAccessToken::class);
}
ANAS MK
  • 71
  • 9
0

You can use following code to remove to token for logged in user.

 $request->user()->token()->revoke();

If you want to learn about this in-depth then watch this tutorial:

https://www.youtube.com/watch?v=UKSQdg1uPbQ

Harish Kumar
  • 218
  • 1
  • 7
-3
public function logout(Request $request)
{
    $request->user()->token()->revoke();

    if ($request->everywhere) {
        foreach ($request->user()->tokens()->whereRevoked(0)->get() as $token) {
            $token->revoke();
        }
    }

    return response()->json(['message' => 'success']);
}