DEV Community

How to log out when using JWT

Arpy Vanyan on June 17, 2018

The wonder of JSON Web Tokens JSON Web Tokens (JWT) is a way of statelessly handling user authentication. What does it mean? Well, JWT...
Collapse
 
alainvanhout profile image
Alain Van Hout

An alternative approach that I've experimented with (so caveats apply) is to keep a 'jwt version number' for each account (in db and/or memory). That is lightweight enough unless you have an enormous amount of users.

Though of course, the first thing to keep in mind is that JWT was simply not designed to work like/with/for this, so any solution we come up with will be a bit of a (conceptual) hack.

Collapse
 
_arpy profile image
Arpy Vanyan

Totally agree :)

Collapse
 
danielescoz profile image
Daniel Escoz

A simple "token blacklist" is not enough. It works for logouts, yes, but there are at least two other events in which you want to invalidate jwts:

  • a "log out all devices" event (manual button, password change)
  • a "user no longer valid" event (user deletion)

For those you need to be able to blacklist users and user-date pairs. The user blacklist can be a simple check against the user database to see if the user exists and is active, the user-date can be a check against a date in the user record indicating the date at which Tokens become valid,and if the token's iat field is before that, it's invalid.

Of course, checking directly in every request is costly and defeats the purpose of jwts, so think carefully if you could just use an opaque token and check against a white list instead of a black list.

Collapse
 
_arpy profile image
Arpy Vanyan

Thanks, Daniel!
The final solution, of course, depends on the app's needs :) But this was definitely a useful addition! Most likely one should handle all those cases in a fully functional app.

Collapse
 
kspeakman profile image
Kasey Speakman

I came to a similar conclusion. If you really must have log out functionality, then you can use a black list. However, using a black list is not a lot different from the old school way of stateful sessions. You still have to lookup the token on every request to be sure it is still valid. So, the blacklist can have a performance impact to the service (or even a bottleneck) just like with session-based auth.

Using refresh tokens could help a little. With them you can implement short-lived auth tokens. For example, if the token expiration is 5 minutes, then you can be sure that a user's permission changes will take 5 mins at most to take effect. However, refresh tokens are considered insecure to keep in the browser, so no help for web apps. (You can do it using HttpOnly cookies, for example, but then getting a new auth token may be visible to the user with redirects.)

And taking refresh tokens to its logical extreme of getting an auth token before every API request... it is no different from looking up a blacklist or looking up an auth session by session identifier. It is still a lookup on every request.

The performance gains come by balancing expiration time with how responsive security changes must be. If security changes must be immediate, then the auth solution becomes stateful and more expensive to scale. No matter which approach you use.

Collapse
 
branislavlazic profile image
Branislav Lazic • Edited

Having refresh tokens is again, similar to saving a session. Yes, the traffic is vastly reduced since a refresh token is checked only when your JWT expires. But still, it doesn't provide any advantage over serialized sessions in terms of scalability. In the end, why have such a complex and potentially insecure architecture when you can simply use cookie + session-based authentication?

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

Tracking sessions and CSRF tokens across servers requires extra infrastructure (something like Redis) for scalability, which is far more expensive than using refresh tokens. The OAuth2 protocol is complicated yes. But since it is an open protocol, there are many libraries to help insulate your code from that complication. And you have to look at what it buys you. You can delegate the hard or tedious parts of security (authentication, password storage, forgot password functions, etc.) to a provider. You also avoid complication in your architecture by not depending on the uptime of the session tracking database and cost by not having to pay for its resource usage.

Collapse
 
polc profile image
Paul Le Corre

Great article! That's one of the reasons it's not recommended to use JWT for session.

See cryto.net/~joepie91/blog/2016/06/1...

Collapse
 
_arpy profile image
Arpy Vanyan

Thanks. And yes, it is :)

Collapse
 
ashraf_hefny profile image
Ashraf Hefny

But JWT has it's own invalidate() that you can use while logging the user out in the backend

vendor/tymon/jwt-auth/src/JWTManager.php
    /**
     * Invalidate a Token by adding it to the blacklist.
     *
     * @param  Token  $token
     * @return bool
     */
    public function invalidate(Token $token)
    {
        if (! $this->blacklistEnabled) {
            throw new JWTException('You must have the blacklist enabled to invalidate a token.');
        }

        return $this->blacklist->add($this->decode($token));
    }
Collapse
 
_arpy profile image
Arpy Vanyan

You are right! It's just a lot of people decide to use JWT and then start wondering how to handle logouts :)
I also suggest thinking on what implementation suits the app best and choose JWT only when it's definitely the best matching solution.

Collapse
 
toddcoulson profile image
Todd Coulson

I'm confused? If we add a blacklist from a db that we have to check, what is the purpose of using JWT at all? Isn't that one of the advantages of have JWT, that you don't need to check the db every time?

Collapse
 
kopseng profile image
Carl-Erik Kopseng

The answer is that JWTs are misused and often are the wrong tool for the job. Like any new thing, a lot of people jump on the bandwagon without analyzing how and why this works. I suggest this really good article for the ups and downs of JWTs and what are good use cases. It taught me loads about JWTs and also scaling in general.

Being logged in is a state. Being logged out is another state - with substates: missing login information and having the wrong login information. One token can only meaningfully represent two of those states: present and not present. You need another (stateful) medium to represent the third, if that is required. These are technology independant facts.

The good thing about blacklists is that they represent a FAR smaller state than the number of valid sessions, so you can probably keep those cached in memory. This is due to

  • there are far fewer people explicitly logging out than are logged in
  • since all JWTs used as access tokens should have an expiration date you can clean the cache for old entries all the time

So it's not that bad :)

Collapse
 
yogeshkhater profile image
Yogesh Khater • Edited

Another solution could be,

  • Store any random salt in user's DB record.
  • While encryption/decryption of the JWT tokens, we can use "your secret" + the salt.
  • On logout, update the salt.

It would make any existing JWT tokens invalid immediately without the need of a blacklist.

Collapse
 
praneetnadkar profile image
Praneet Nadkar

Thanks for posting this. I was looking to do the same with JWT.