Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extending RefreshToken method to use customized token claims #481

Open
Elijer opened this issue Oct 20, 2021 · 4 comments
Open

Extending RefreshToken method to use customized token claims #481

Elijer opened this issue Oct 20, 2021 · 4 comments

Comments

@Elijer
Copy link

Elijer commented Oct 20, 2021

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.

@taylornelson-outside
Copy link

This is the same as my question.

@ademidun
Copy link

ademidun commented Jan 19, 2023

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.

@ayshark
Copy link

ayshark commented Feb 11, 2023

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

@koleror
Copy link

koleror commented Apr 15, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants