19

I want to ask if it's a good idea to logout when I'm using JWT. To log in, I send a post request with username and password to get the desired token (saved into localStorage) which will allow me to send further requests to views that requires the token, of course.

But I'm not sure how should I log out the user. I can clear the localStorage, but the token remains available.

So, I want to ask if I should use refresh the token since I can not disable it.

Olivier Pons
  • 15,363
  • 26
  • 117
  • 213
yierstem
  • 1,933
  • 5
  • 21
  • 42

4 Answers4

18

You are right, even after you remove the JWT token it remains valid token for a period of time until it expires. JWT is stateless. So if you want to handle logout and to invalidate token you must need to keep a database or in memory cache to store the invalid(blacklisted) token. Then you need to add a new permission to check whether the token is blacklisted or not.

class BlackListedToken(models.Model):
    token = models.CharField(max_length=500)
    user = models.ForeignKey(User, related_name="token_user", on_delete=models.CASCADE)
    timestamp = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("token", "user")


class IsTokenValid(BasePermission):
    def has_permission(self, request, view):
        user_id = request.user.id            
        is_allowed_user = True
        token = request.auth.decode("utf-8")
        try:
            is_blackListed = BlackListedToken.objects.get(user=user_id, token=token)
            if is_blackListed:
                is_allowed_user = False
        except BlackListedToken.DoesNotExist:
            is_allowed_user = True
        return is_allowed_user

You can remove the token from the blacklisted list after its expiry.

a_k_v
  • 1,558
  • 7
  • 18
  • 8
    If I have to call database on every call then what's the diff. b/w drf token authentication, then ain't it drf token and jwt same – Nikhil Bhardwaj Feb 02 '20 at 09:12
  • We don't need to store every token in the database/memory. We just need to hold the valid tokens till they expire. The best way is to use some caching mechanism and you can auto-expire the token by setting the TTL. – a_k_v Aug 01 '23 at 07:26
10

You cannot manually expire a token after it has been created. Thus, you cannot actually log out with JWT on the server side as you do with sessions.

JWT is stateless, meaning that you should store everything you need in the payload and skip performing a DB query on every request. But if you plan to have a strict log out functionality, that cannot wait for the token auto-expiration, even though you have cleaned the token from the client-side, then you might need to neglect the stateless logic and do some queries. so what's a solution?

  • Set a reasonable expiration time on tokens

  • Delete the stored token from client-side upon log out

  • Query provided token against The Blacklist on every authorized request

The Blacklist

“Blacklist” of all the tokens that are valid no more and have not expired yet. You can use a DB that has TTL option on documents that would be set to the amount of time left until the token is expired.

Redis

Redis is a good option for blacklist, which will allow fast in-memory access to the list. Then, in the middleware of some kind that runs on every authorized request, you should check if the provided token is in The Blacklist. If it is you should throw an unauthorized error. And if it is not, let it go and the JWT verification will handle it and identify if it is expired or still active.

For more information, see How to log out when using JWT. by Arpy Vanyan

Jamil Noyda
  • 3,321
  • 2
  • 21
  • 26
  • 3
    about the redis you mentioned it great, But redis is in memory, So if server restarts, everything will be lost and again every token is valid – Reza Torkaman Ahmadi Dec 26 '18 at 15:58
  • 1
    @RezaTorkamanAhmadi What if we run Redis in seperate cluster? In that way, it will be maintained separately and then we don't need to worry about server restart problem. – reeversedev Dec 27 '18 at 07:29
  • 1
    yeah, that's a great option too. I myself solve it with a custom method of jwt and a lookup id in payload to be verified with database check. But this redis method could be much faster. But the project should be in a good scale, cause in small to medium size projects, I guess it gives complexity to project – Reza Torkaman Ahmadi Dec 27 '18 at 07:57
3

Every JWT which you issue should have an expiry datetime, so whenever you are logging out the user you should delete the jwt-token from the localstorage cookie.

but the token remains available.

Not sure what the above line means but you should not worry about if the token remains available to the user or not after you clear it from localstorage and cookie because either way it would get invalid after the expiry date.

Arghya Saha
  • 5,599
  • 4
  • 26
  • 48
  • 10
    The issue of this " token remains available" is even after you logout from a session the token is valid for a particular amount of time(Before its expiry). So some one possess your token can successfully verify and can sign in to the system. Its a security flow! – a_k_v Dec 03 '18 at 06:23
1

A simpler way of achieving this will be via using the rest_framework_simplejwt package. I believe you've also used the same package for JWT generation as well.

While the user is performing logout, you need to clear the cache from the frontend, and also need to add the refresh token to a blacklist in the backend.

Access tokens are short-lived and do not need to be blacklisted, it is preferred to have minimal lifespan for the access tokens. So that they will eventually expire.

rest_framework_simplejwt.token_blacklist will only blacklist the refresh tokens by default.

All you need to do is add the following app on your settings.py INSTALLED_APPS.

INSTALLED_APPS = (
    'rest_framework_simplejwt.token_blacklist',
)

And also configure the urls.py for the TokenBlacklistView

from rest_framework_simplejwt.views import TokenBlacklistView

    urlpatterns = [
      ...
      path('logout/', TokenBlacklistView.as_view(), name='token_blacklist'),
      ...
    ]

Source: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/blacklist_app.html

JISHNU N
  • 9
  • 1
  • 5