djangorestframework-simplejwt icon indicating copy to clipboard operation
djangorestframework-simplejwt copied to clipboard

Extending RefreshToken method to use customized token claims

Open Elijer opened this issue 3 years ago • 4 comments

I used the docs straightforward instructions on how to extend the get_token method of MyTokenObtainSerializer to include a custom claim in the JWT token it produced. However, I am trying to extend the RefreshToken.for_user() method in the same way and I'm not sure how to go about doing it. Admittedly, my understanding of inheritance in python is tenuous.

Elijer avatar Oct 20 '21 18:10 Elijer

This is the same as my question.

taylornelson-outside avatar Dec 14 '21 23:12 taylornelson-outside

Solution from @jayps

Summary: Change RefreshToken.for_user(user) to CustomTokenObtainPairSerializer.get_token(user)

For anyone else finding this: I achieved by using a custom serializer as described in the docs. I have my serializer like this:

class AuthTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)

        token['name'] = user.first_name
        # Add other claims here as required
        return token

And then in my view where I manually create a new token:

user = User.objects.get(username='example')
# Optionally perform some operations against the user.
refresh = AuthTokenObtainPairSerializer.get_token(user)

return Response(data={'refresh': str(refresh), 'access': str(refresh.access_token)})

In my case, this successfully returns a fresh token with new claims.

ademidun avatar Jan 19 '23 01:01 ademidun

in RefreshToken.for_user(user), can't I pass any other parameter I want to include in the payload?

ayshark avatar Feb 11 '23 16:02 ayshark

I'm having the same problem. I have 2 problems with the solution of overriding the serializer:

  • The custom claims are not stored in the OutstandingToken
  • The doc has a section explaining how to manually create a token. Which seems clean (I need it for unit test purpose on my side), but the problem is the custom claims added from the serializer are therefore not included, making this unusable.

So, I thought about Subclassing RefreshToken.for_user to add my custom claims, but again, the OutstandingToken does not include the custom claims.

From my point of view, the easier solution would be add a hook at the end of the for_user method which would only be made for custom claims injection. Something like:

@classmethod
def for_user(cls, user):
    """
    Returns an authorization token for the given user that will be provided
    after authenticating the user's credentials.
    """
    user_id = getattr(user, api_settings.USER_ID_FIELD)
    if not isinstance(user_id, int):
        user_id = str(user_id)

    token = cls()
    token[api_settings.USER_ID_CLAIM] = user_id
    token = cls.for_user_post_hook(user, token)
    return token

def for_user_post_hook(cls, User, token):
    # does nothing by default, but allows custom behaviours
    return token

What do you think about it?

That being said, I believe storing the full tokens in DB seems like a security risk, as someone already pointed out. I believe we should only store the jti and token expiration, and maybe the sub for filtering purpose? But that's another subject.

I'd be happy to make a PR for that if you'd like.

koleror avatar Apr 15 '23 14:04 koleror