coderDude
coderDude

Reputation: 904

How to revoke refresh token on password reset?

I am using django-rest-framework-simplejwt to get access token and refresh token .

The problem is that refresh token is not becoming invalid if I change the password of the user. Basically I can continue to send refresh token and get new access tokens even after user has changed password.

What I would like instead is to ask user to re-submit the username and new password to get a new pair of access and refresh tokens.

How would I accomplish this?



PS: Just because I am curious, shouldn't this be the default behaviour of the library? In what case would we want to retain the refresh token after credentials have changed?

Upvotes: 1

Views: 2840

Answers (2)

Waseem Kn
Waseem Kn

Reputation: 76

There is a new update in the django rest framework simpljwt (v5.3.1) package that resolved this issue easily.

You can add this settings to the SIMPLE_JWT django settings variable:

SIMPLE_JWT = {
    ...,
    "CHECK_REVOKE_TOKEN": True
}

And it will automatically invalidate the access token once a user change his password.

And also the refresh token will be invalid but without errors, I mean you will get a new access token each time you request it by the refresh but it will be invalid.

You can check the pull request of this solution if you need to know more about how this is implemented in the package code.

Upvotes: 0

coderDude
coderDude

Reputation: 904

I figured how to get this working.
What I am did is put a signal that tracks if any required parameter has changed. If so, it blacklists all the refresh tokens associated with that user.
Here is the code:

First add 'rest_framework_simplejwt.token_blacklist' in installed apps. Then:

@receiver(signals.pre_save, sender=User)
def revoke_tokens(sender, instance, update_fields, **kwargs):
    if not instance._state.adding: #instance._state.adding gives true if object is being created for the first time
        existing_user = User.objects.get(pk=instance.pk)
        if instance.password != existing_user.password or instance.email != existing_user.email or instance.username != existing_user.username:
        # If any of these params have changed, blacklist the tokens
              outstanding_tokens = OutstandingToken.objects.filter(user__pk=instance.pk)
              # Not checking for expiry date as cron is supposed to flush the expired tokens
              # using manage.py flushexpiredtokens. But if You are not using cron, 
              # then you can add another filter that expiry_date__gt=datetime.datetime.now()

              for out_token in outstanding_tokens:
                   if hasattr(out_token, 'blacklistedtoken'):
                       # Token already blacklisted. Skip
                       continue

                       BlacklistedToken.objects.create(token=out_token)

WHat this code basically does is , gets all outstanding tokens for the user, then adds all of them to blacklist. You can get more info on outstanding/blacklisted tokens here. https://github.com/davesque/django-rest-framework-simplejwt#blacklist-app

Upvotes: 6

Related Questions