I'm using Django 3.2 with the django.auth.contrib app and djangorestframework-jwt==1.11.0. How do I prolong/reissue a new session token upon receiving a request for an authenticated resource and validating the user can access that resource? I use the following serializer and view to login the user and issue the initial token
class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=255)
password = serializers.CharField(max_length=128, write_only=True)
token = serializers.CharField(max_length=255, read_only=True)
def validate(self, data):
username = data.get("username", None)
password = data.get("password", None)
user = authenticate(username=username, password=password)
if user is None:
raise serializers.ValidationError(
'A user with this email and password is not found.'
)
try:
payload = JWT_PAYLOAD_HANDLER(user)
jwt_token = JWT_ENCODE_HANDLER(payload)
update_last_login(None, user)
except User.DoesNotExist:
raise serializers.ValidationError(
'User with given email and password does not exists'
)
return {
'username':user.username,
'token': jwt_token
}
class UserLoginView(RetrieveAPIView):
permission_classes = (AllowAny,)
serializer_class = UserLoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
response = {
'success' : 'True',
'status code' : status.HTTP_200_OK,
'message': 'User logged in successfully',
'token' : serializer.data['token'],
}
status_code = status.HTTP_200_OK
return Response(response, status=status_code)
I have this in my settings file to keep the session to 1 hour initially
JWT_AUTH = {
# how long the original token is valid for
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=1),
}
The client submits the session token in the "Authorization" header and it is validated (for example) using the below view
class UserProfileView(RetrieveAPIView):
permission_classes = (IsAuthenticated,)
authentication_class = JSONWebTokenAuthentication
def get(self, request):
try:
token = get_authorization_header(request).decode('utf-8')
if token is None or token == "null" or token.strip() == "":
raise exceptions.AuthenticationFailed('Authorization Header or Token is missing on Request Headers')
decoded = jwt.decode(token, settings.SECRET_KEY)
username = decoded['username']
status_code = status.HTTP_200_OK
response = {
'success': 'true',
'status code': status_code,
'message': 'User profile fetched successfully',
'data': {
#...
}
}
except Exception as e:
status_code = status.HTTP_400_BAD_REQUEST
response = {
'success': 'false',
'status code': status.HTTP_400_BAD_REQUEST,
'message': 'User does not exists',
'error': str(e)
}
return Response(response, status=status_code)
What I would like to do in my response is send a new session token down to the user that is good for another hour but I'm unclear what call I need to make to generate such a token and/or edit/invalidate the existing one.
It is not possible to change a JWT after it is issued, so you can not extend its lifetime, but you can do something like this:
for every request client makes:
if JWT is expiring:
generate a new JWT and add it to the response
And the client will use this newly issued token.
for this, you can add a django middleware:
**EDITED
class ExtendJWTToResponse:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
jwt_token = get_authorization_header(request).decode('utf-8')
new_jwt_token = None
try:
payload = jwt.decode(jwt_token, settings.SECRET_KEY)
new_jwt_token = JWT_ENCODE_HANDLER(payload)
except PyJWTError:
pass
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
if new_jwt_token:
response['Refresh-Token'] = new_jwt_token
return response
And the client must check on 'Refresh-Token' header on response and if there is any it should replace the token and use the newly issued token with the extended lifetime.
note: it is better to throttle issuing new tokens, for example, every time the request token is going to expire in the next 20 minutes...