Open tomcajot opened 1 year ago
I accidentally dived into the source code and found something that might be relevant to your problem.
The issue I encountered is after I set "rotate_refresh_token = True", every time when my fresh token updated with a new "iat", the corresponding access token still carry the "iat" value from the previous refresh token.
After reviewing the code, I found the issue is from tokens.py, in the class RefreshToken(BlacklistMixin, Token):
@property
def access_token(self):
"""
Returns an access token created from this refresh token. Copies all
claims present in this refresh token to the new access token except
those claims listed in the `no_copy_claims` attribute.
"""
access = self.access_token_class()
# Use instantiation time of refresh token as relative timestamp for
# access token "exp" claim. This ensures that both a refresh and
# access token expire relative to the same time if they are created as
# a pair.
access.set_exp(from_time=self.current_time)
no_copy = self.no_copy_claims
for claim, value in self.payload.items():
if claim in no_copy:
continue
access[claim] = value
return access
When getting the Access token after refreshing, it copy some claims from the current Refresh token.
and in the serializers.py, in the class TokenRefreshSerializer(serializers.Serializer):
def validate(self, attrs):
refresh = self.token_class(attrs["refresh"])
data = {"access": str(refresh.access_token)}
if api_settings.ROTATE_REFRESH_TOKENS:
if api_settings.BLACKLIST_AFTER_ROTATION:
try:
# Attempt to blacklist the given refresh token
refresh.blacklist()
except AttributeError:
# If blacklist app not installed, `blacklist` method will
# not be present
pass
refresh.set_jti()
refresh.set_exp()
refresh.set_iat()
data["refresh"] = str(refresh)
return data
The access token remains some "copied" claims with the "outdated" refresh token, and doesn't renew with the "new" refresh token.
I guess there should be a possibility that the no_copy_claims could be configured. Hope this helps.
Would be great if we can get some info on this by the devs. Faced the same issue recently
I solved it by a custom refresh token view serializer that uses a RefreshToken subclass. The access_token
function of this subclass finds the uid, gets the user from db, and recreates a new refresh token and returns the access token off to that. Wondered if there was a better way than overriding classes this drastically. I don't like that I'm creating a new refresh token out and only using it's access token. Not an expert on JWT but I would guess that there is some configuration where access and refresh are linked and this code of mine will destroy the authentication in that configuration :D
Something like "FORCE_REEVALUATE_ACCESS_ON_REFRESH" which can be enabled and clearly known to be more expensive since it makes more db calls I guess.
This is my code by the way:
class MyRefreshToken(RefreshToken):
@property
def access_token(self) -> typing.Optional[AccessToken]:
"""
Returns an access token created from this refresh token. Copies all
claims present in this refresh token to the new access token except
those claims listed in the `no_copy_claims` attribute.
# TODO: Surely there is a better way to do this by avoiding overriding of classes. Couldn't find it for now
"""
uid = self.payload.get("uid")
if uid:
user: User = User.objects.get(pk=uid)
refresh = MyTokenObtainPairSerializer.get_token(user)
return refresh.access_token
return super().access_token
class MyTokenRefreshSerializer(TokenRefreshSerializer):
token_class = MyRefreshToken
I'm currently facing an issue where, after I refresh my token, the claims aren't being updated and the value is outdated. Until I create a new token by signin a user in. I simply implemented a custom TokenObtainPairSerializer like the docs recommend. Is there a specific setting I should change in order to have the claims be regenerated when the token gets updated? Please tell me if there is any piece of code you'd like to check. Thanks.