Djangorestframework-simplejwt: Blacklist access token

Created on 27 Feb 2020  路  15Comments  路  Source: jazzband/djangorestframework-simplejwt

How to blacklist access token along with refresh token, only able to blacklist refresh token?

Most helpful comment

Suppose we make a Logout function that adds a refresh token to the blacklist. Then how do I display the login button on the frontend if the user still has a valid access token. Or is it a frontend task? After running the logout function, should the frontend draw the login button?

Logout from back end

For the first part (the logout function) it's pretty easy: you need a new API endpoint. Create a new View inherited from TokenRefreshView that uses a new token serializer that inherits from TokenRefreshSerializer.

In this serializer (see the source code for inspiration), inside the validation method, you can manually blacklist it with:

token = RefreshToken(response.data["refresh"])
token.blacklist()

Then you can decode the refresh token with:

token_payload = token_backend.decode(attrs['refresh'])

To obtain the user (the refresh contains the user's id, so you need to call the db) and call logout(user) (from django.contrib.auth import logout) if you have Django sessions and other things to do.

Front end

To recap, proper front end API calls are (simplified):

  1. call protected endpoints using the access token obtained at login time.
  2. if HTTP 401 call the refresh endpoint to obtain a new access and refresh token.

    • if also this has HTTP 401 then redirect to log in (and cleanup the stored tokens etc). The 401 on refresh endpoints means you cannot refresh your token (refresh expired, blacklisted, or whatever)

  3. else, now you have a new access and refresh token: retry the call (1) with the new access token.

So, the logout button in the front end must call the aforementioned endpoint and redirect the user to the log in page. Also, in case of logout the front end must destroy all stored access and refresh tokens.

Anyway, when the refresh token has been blacklisted, implies an HTTP 401 on the next attempt to refresh the token from the front end. As usual, if your refresh call has a HTTP 401 you must redirect the user to the log in page.

All 15 comments

The idea behind JWT is to keep the access token lifetime very low (minutes) so that you don't need to blacklist them, as they will naturally expire very soon. You just need to blacklist the refresh tokens, thus making impossible to obtain new access tokens without a new authentication.

Does this mean that in the ACCESS_TOKEN_LIFETIME settings it makes sense to specify a minimum time, for example 1 minute or less?

Does this mean that in the ACCESS_TOKEN_LIFETIME settings it makes sense to specify a minimum time, for example 1 minute or less?

Yes. You have to calibrate the time because if your access token expires, then you have to automatically (from the front end code) request a refresh token and retry the failed request. So, if you set a time too small you have an huge amount of failures and retries, if you set a time too high, you may have security issues (the token is valid but the user shouldn't). In my projects (API consumed by web apps and mobile apps) I usually set 5 minutes.

Suppose we make a Logout function that adds a refresh token to the blacklist. Then how do I display the login button on the frontend if the user still has a valid access token. Or is it a frontend task? After running the logout function, should the frontend draw the login button?

Suppose we make a Logout function that adds a refresh token to the blacklist. Then how do I display the login button on the frontend if the user still has a valid access token. Or is it a frontend task? After running the logout function, should the frontend draw the login button?

Logout from back end

For the first part (the logout function) it's pretty easy: you need a new API endpoint. Create a new View inherited from TokenRefreshView that uses a new token serializer that inherits from TokenRefreshSerializer.

In this serializer (see the source code for inspiration), inside the validation method, you can manually blacklist it with:

token = RefreshToken(response.data["refresh"])
token.blacklist()

Then you can decode the refresh token with:

token_payload = token_backend.decode(attrs['refresh'])

To obtain the user (the refresh contains the user's id, so you need to call the db) and call logout(user) (from django.contrib.auth import logout) if you have Django sessions and other things to do.

Front end

To recap, proper front end API calls are (simplified):

  1. call protected endpoints using the access token obtained at login time.
  2. if HTTP 401 call the refresh endpoint to obtain a new access and refresh token.

    • if also this has HTTP 401 then redirect to log in (and cleanup the stored tokens etc). The 401 on refresh endpoints means you cannot refresh your token (refresh expired, blacklisted, or whatever)

  3. else, now you have a new access and refresh token: retry the call (1) with the new access token.

So, the logout button in the front end must call the aforementioned endpoint and redirect the user to the log in page. Also, in case of logout the front end must destroy all stored access and refresh tokens.

Anyway, when the refresh token has been blacklisted, implies an HTTP 401 on the next attempt to refresh the token from the front end. As usual, if your refresh call has a HTTP 401 you must redirect the user to the log in page.

@LordGordonQ Great, very helpful. Thank you!

@LordGordonQ the question is not exactly on the subject, but could you tell me if it's possible to redirect to the custom page?
Default django way doesnt work
LOGIN_REDIRECT_URL = 'administartor_dashboard'
Or its a frontend task make redirect after successfully obtaining a token?

And besides is it possible to use emai+password instead username+password to get a token?

Default django way doesnt work
LOGIN_REDIRECT_URL = 'administartor_dashboard'
Or its a frontend task make redirect after successfully obtaining a token?

this is quite offtopic, better ask on StackOverflow (it has already been replied many times)

And besides is it possible to use emai+password instead username+password to get a token?

depends on how is is configured your User model and your authentication backend. Check Django's documentation for further information, or ask StackOverflow. As example: in my project I usually create a custom User where the id is the email, and not the username, so it is automatic.

@LordGordonQ Thank you so much for your help.

It's difficult to get access token in every 5 mins for a front-end guy. Help me out if i make it to 2 hrs or give me some suggestions what can be done for better security purpose because i can't go with default 5 mins.

To change the default settings, just set the desired time for
ACCESS_TOKEN_LIFETIME and REFRESH_TOKEN_LIFETIME configurations.

But I guess your issue is not in the time itself: it's in the front end code. I'm afraid that discussion of front end code is out of scope here (also much depend what front end are using). I strongly suggest you to ask this on StackOverflow for the proper front end you are using. BTW, for every main front end this is a common topic, so you can find plenty of examples on how to consume and refresh JWT.

@LordGordonQ ...Yeah but can you just tell me one reason of not blacklisting access token along with refresh token? Any specific reason?

@LordGordonQ ...Yeah but can you just tell me one reason of not blacklisting access token along with refresh token? Any specific reason?

JWT was designed as to run at a scale in a decentralized system. This implies avoiding hitting a single source of truth (the datastore) at every request.

Maybe you should first ask yourself what are your need, and then choose the proper authentication mechanism that better fits your need. JWT is one of the many solutions, with its strengths and weakness. And it is suitable for specific use cases.

You can read more on JWT use cases here (first part): https://github.com/dentarg/blog/blob/master/_posts/2014-01-07-angularjs-authentication-with-cookies-vs-token.markdown

@LordGordonQ ..Ok ,let me explore me more about it. BTW thanks man for the suggestions and help.

This seems like an issue with the way that someone is choosing to construct their front-end app. I'm going to close this for now. If anyone thinks this can be interpreted as an issue with simple jwt, feel free to re-open.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mjackstone6 picture mjackstone6  路  4Comments

zhangxu3486432 picture zhangxu3486432  路  5Comments

mosi-kha picture mosi-kha  路  6Comments

robd003 picture robd003  路  6Comments

jesusjiib picture jesusjiib  路  7Comments