Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token immediately blacklisted #983

Open
njt1982 opened this issue Jan 29, 2017 · 23 comments
Open

Token immediately blacklisted #983

njt1982 opened this issue Jan 29, 2017 · 23 comments

Comments

@njt1982
Copy link

njt1982 commented Jan 29, 2017

I'm upgrading my Laravel 5.2 app to Lumen 5.4 and have managed to repair my app to a point where I can at least log in again ;)

So on login, I get a token back and my Angular app stores it and makes 3 API requests with it. These all work perfectly.

Then I click something to make another request. This request fails with:

The token has been blacklisted

I can confirm the correct header is being sent:

Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9............

I get 2 stack traces... 1/2 is this:

TokenBlacklistedException in Manager.php line 97:
The token has been blacklisted

in Manager.php line 97
at Manager->decode(object(Token)) in JWT.php line 183
at JWT->getPayload() in JWTAuth.php line 60
at JWTAuth->authenticate() in BaseMiddleware.php line 69
at BaseMiddleware->authenticate(object(Request)) in Authenticate.php line 30
at Authenticate->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 102
at Pipeline->then(object(Closure)) in RoutesRequests.php line 779
at Application->sendThroughPipeline(array('Tymon\\JWTAuth\\Http\\Middleware\\Authenticate', 'Tymon\\JWTAuth\\Http\\Middleware\\RefreshToken'), object(Closure)) in RoutesRequests.php line 625
at Application->handleFoundRoute(array(true, array('uses' => 'App\\Http\\Controllers\\AuctionController@index', 'middleware' => array('jwt.auth', 'jwt.refresh')), array())) in RoutesRequests.php line 528
at Application->Laravel\Lumen\Concerns\{closure}() in RoutesRequests.php line 782
at Application->sendThroughPipeline(array(), object(Closure)) in RoutesRequests.php line 534
at Application->dispatch(null) in RoutesRequests.php line 475
at Application->run() in index.php line 35

Then 2/2 is this:

UnauthorizedHttpException in BaseMiddleware.php line 71:
The token has been blacklisted
in BaseMiddleware.php line 71
at BaseMiddleware->authenticate(object(Request)) in Authenticate.php line 30
at Authenticate->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 102
at Pipeline->then(object(Closure)) in RoutesRequests.php line 779
at Application->sendThroughPipeline(array('Tymon\\JWTAuth\\Http\\Middleware\\Authenticate', 'Tymon\\JWTAuth\\Http\\Middleware\\RefreshToken'), object(Closure)) in RoutesRequests.php line 625
at Application->handleFoundRoute(array(true, array('uses' => 'App\\Http\\Controllers\\AuctionController@index', 'middleware' => array('jwt.auth', 'jwt.refresh')), array())) in RoutesRequests.php line 528
at Application->Laravel\Lumen\Concerns\{closure}() in RoutesRequests.php line 782
at Application->sendThroughPipeline(array(), object(Closure)) in RoutesRequests.php line 534
at Application->dispatch(null) in RoutesRequests.php line 475
at Application->run() in index.php line 35

As you can see in there, I am using jwt.auth and jwt.refresh middleware...

$app->group(['prefix' => 'api/v1'], function() use ($app) {
  $app->post('login', 'AuthController@login');

  $app->group(['middleware' => ['jwt.auth', 'jwt.refresh']], function() use ($app) {
  ...
  ...

Any tips?

@njt1982
Copy link
Author

njt1982 commented Jan 29, 2017

Note: I can make the error go away by adding this to my .env:

JWT_BLACKLIST_ENABLED=false

But this does not feel like a "fix"... This feels like I have just made the problem go away without actually solving the cause ;)

@Mando-Chris
Copy link

Hi @njt1982 , I am no expert, but I was just poking around in the code in this area. And the code that does the refresh (Manager->refresh) will blacklist the token immediately if you have that enabled, making the tokens "one use only".

        if ($this->blacklistEnabled) {
            // invalidate old token
            $this->invalidate($token, $forceForever);
        }

The refresh middleware seems to only be something you would use if you were doing one use tokens.

Again, I'm no expert, currently poking around the code trying to figure out the right way to add refresh to my project.

@hsklia
Copy link

hsklia commented Feb 12, 2017

i have this issue too

@Mando-Chris
Copy link

@njt1982 Are you storing the refreshed token?

@philperron
Copy link

philperron commented Apr 14, 2017

Encountered the same problem, @njt1982 don't konw if this is still a problem, but for anyone else in this situation.

From the wiki

RefreshToken
This middleware will again try to parse the token from the request, and in turn, will refresh the token (thus invalidating the old one) and return it as part of the next response. This essentially yields a single use token flow, which reduces the window of attack if a token is compromised since it is only valid for the single request.

So it is by design.

From my understanding, you won't be able to combine jwt.auth and jwt.refresh for the same route, that's what I was trying to do at least. Use only jwt.auth and once your frontend detects that the token is invalid, call a route to refresh it, using the refresh middleware or directly the refresh function from the manager.

@garhbod
Copy link

garhbod commented Sep 5, 2017

I was having the same issue. Was able to overcome it not by setting

JWT_BLACKLIST_ENABLED=false

Like @njt1982 mentioned as that would open up a vulnerability but I set the blacklist_grace_period to 30 for 30 seconds

JWT_BLACKLIST_GRACE_PERIOD=30

Solved the issue of tokens dying for me and kept the security of having a blacklist

@garhbod
Copy link

garhbod commented Sep 5, 2017

Okay this only fixed the issue for 30 seconds for me delaying the inevitable 401 - Token has been black listed

I now have two options as I see it.

  1. make a custom refresh middleware or closure where the refresh is called once and simple refreshes the token but doesn't blacklist it straight away
  2. refresh after or before every request

Thoughts?

@garhbod
Copy link

garhbod commented Sep 6, 2017

Okay the solution for me was the following. I was even able to change the JWT_BLACKLIST_GRACE_PERIOD back to 0.

I changed my refresh api endpoint from

            $api->get('refresh', [ 'middleware' => 'jwt.refresh',
                function() {
                    return response()
                        ->json([ 'message' => 'Token refreshed!' ]);
                }
            ]);

to

            $api->get('refresh', [ 'middleware' => 'jwt.refresh',
                function() {
                    return response()
                        ->json([ 'message' => 'Token refreshed!' ])
                        ->header('Cache-Control', 'no-cache, no-store, must-revalidate');
                }
            ]);

This prevents the endpoint being cached by the browser which means that the new/refreshed token is always received.

@acidjazz
Copy link

acidjazz commented Sep 6, 2017

having all of these issues as well, @garhbod's cache method sort of worked but I can still get my token blacklisted sometimes when trying to refresh it.

@garhbod
Copy link

garhbod commented Sep 6, 2017

@acidjazz What frontend are you using? Are multiple requests happening at once?

@acidjazz
Copy link

acidjazz commented Sep 6, 2017

@garhbod nuxtjs, I don't think so, I can duplicate the blacklisting event by manually logging in, waiting for it to expire and hitting /refresh either with my browser or postman

@ivahidmontazer
Copy link

please note that you can change config options in your codes in run time. I used this code and this was a solution for me:

        config([
            'jwt.blacklist_enabled' => true
        ]);
        auth()->logout();
        JWTAuth::invalidate(JWTAuth::parseToken());

@pwwat
Copy link

pwwat commented Nov 4, 2018

please note that you can change config options in your codes in run time. I used this code and this was a solution for me:

        config([
            'jwt.blacklist_enabled' => true
        ]);
        auth()->logout();
        JWTAuth::invalidate(JWTAuth::parseToken());

@ivahidmontazer where to change there ?

@ivahidmontazer
Copy link

please note that you can change config options in your codes in run time. I used this code and this was a solution for me:

        config([
            'jwt.blacklist_enabled' => true
        ]);
        auth()->logout();
        JWTAuth::invalidate(JWTAuth::parseToken());

@ivahidmontazer where to change there ?

You can change this config in your controllers or everywhere else, run time .

@shahid-hussain009
Copy link

shahid-hussain009 commented Dec 15, 2018 via email

@ivahidmontazer
Copy link

ivahidmontazer commented Dec 16, 2018

I have used this but not work

On Sat, Dec 15, 2018, 02:50 Vahid Montazer @.*** wrote: please note that you can change config options in your codes in run time. I used this code and this was a solution for me: config([ 'jwt.blacklist_enabled' => true ]); auth()->logout(); JWTAuth::invalidate(JWTAuth::parseToken()); @ivahidmontazer https://github.com/ivahidmontazer where to change there ? You can change this config in your controllers or everywhere else, run time . — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#983 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/Aj_EwctcWst5U6kAuf5d2cha4Nhxottlks5u5B0ZgaJpZM4LwqTX .

I had same problem specially when using multi auth with jwt. You can use a try, catch. when exception happens you can return 'You have logged out successfully'; in catch section.
I know this way user will not actually be logged out, but only solution I have found, was this!

@shahid-hussain009
Copy link

shahid-hussain009 commented Dec 17, 2018

nothing work i am using this code in my controller on run time

try 
        {
            config([
            'jwt.blacklist_enabled' => true
            ]);
            \Cookie::forget(JWTAuth::parseToken());
            auth()->logout();
            JWTAuth::invalidate(JWTAuth::parseToken());
            return response()->json(['message' => 'Successfully logged out']);
            
        } 
        catch (Exception $e) 
        {
            return response()->json(['message' => 'There is something wrong try again']);
        }

@ivahidmontazer
Copy link

nothing work i am using this code in my controller on run time

try 
        {
            config([
            'jwt.blacklist_enabled' => true
            ]);
            \Cookie::forget(JWTAuth::parseToken());
            auth()->logout();
            JWTAuth::invalidate(JWTAuth::parseToken());
            return response()->json(['message' => 'Successfully logged out']);
            
        } 
        catch (Exception $e) 
        {
            return response()->json(['message' => 'There is something wrong try again']);
        }

I set return response()->json(['message' => 'Successfully logged out']); in catch section not in try section. I know this way user will not be logged out, but I didn't fount any other solution.

@shahid-hussain009
Copy link

shahid-hussain009 commented Dec 17, 2018

then what is the good of using

return response()->json(['message' => 'Successfully logged out']);

this code is catch section this way it shows only logout message my aim is not to show only message but logout the user as well
what a funny answer it is but thanks for your response it will never work.

@stale
Copy link

stale bot commented Dec 26, 2020

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the stale label Dec 26, 2020
@Messhias
Copy link

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

Yes, it's still relevant and not fixed.

@Messhias
Copy link

Messhias commented Jul 2, 2021

O opened PR #2139 to try to solve this issue and mitigate this exception and make it optional for us.

@Messhias
Copy link

Messhias commented Jul 2, 2021

@tymondesigns can you review my PR #2139?

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants