Token Authentication for RESTful API: should the token be periodically changed? - django

I'm building a RESTful API with Django and django-rest-framework.
As authentication mechanism we have chosen "Token Authentication" and I have already implemented it following Django-REST-Framework's documentation, the question is, should the application renew / change the Token periodically and if yes how? Should it be the mobile app that requires the token to be renewed or the web-app should do it autonomously?
What is the best practice?
Anybody here experienced with Django REST Framework and could suggest a technical solution?
(the last question has lower priority)

It is good practice to have mobile clients periodically renew their authentication token. This of course is up to the server to enforce.
The default TokenAuthentication class does not support this, however you can extend it to achieve this functionality.
For example:
from rest_framework.authentication import TokenAuthentication, get_authorization_header
from rest_framework.exceptions import AuthenticationFailed
class ExpiringTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
try:
token = self.model.objects.get(key=key)
except self.model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
# This is required for the time comparison
utc_now = datetime.utcnow()
utc_now = utc_now.replace(tzinfo=pytz.utc)
if token.created < utc_now - timedelta(hours=24):
raise exceptions.AuthenticationFailed('Token has expired')
return token.user, token
It is also required to override the default rest framework login view, so that the token is refreshed whenever a login is done:
class ObtainExpiringAuthToken(ObtainAuthToken):
def post(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.validated_data['user'])
if not created:
# update the created time of the token to keep it valid
token.created = datetime.datetime.utcnow()
token.save()
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
obtain_expiring_auth_token = ObtainExpiringAuthToken.as_view()
And don't forget to modify the urls:
urlpatterns += patterns(
'',
url(r'^users/login/?$', '<path_to_file>.obtain_expiring_auth_token'),
)

If someone is interested by that solution but wants to have a token that is valid for a certain time then gets replaced by a new token here's the complete solution (Django 1.6):
yourmodule/views.py:
import datetime
from django.utils.timezone import utc
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from django.http import HttpResponse
import json
class ObtainExpiringAuthToken(ObtainAuthToken):
def post(self, request):
serializer = self.serializer_class(data=request.DATA)
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.object['user'])
utc_now = datetime.datetime.utcnow()
if not created and token.created < utc_now - datetime.timedelta(hours=24):
token.delete()
token = Token.objects.create(user=serializer.object['user'])
token.created = datetime.datetime.utcnow()
token.save()
#return Response({'token': token.key})
response_data = {'token': token.key}
return HttpResponse(json.dumps(response_data), content_type="application/json")
return HttpResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
obtain_expiring_auth_token = ObtainExpiringAuthToken.as_view()
yourmodule/urls.py:
from django.conf.urls import patterns, include, url
from weights import views
urlpatterns = patterns('',
url(r'^token/', 'yourmodule.views.obtain_expiring_auth_token')
)
your project urls.py (in the urlpatterns array):
url(r'^', include('yourmodule.urls')),
yourmodule/authentication.py:
import datetime
from django.utils.timezone import utc
from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions
class ExpiringTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
try:
token = self.model.objects.get(key=key)
except self.model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
utc_now = datetime.datetime.utcnow()
if token.created < utc_now - datetime.timedelta(hours=24):
raise exceptions.AuthenticationFailed('Token has expired')
return (token.user, token)
In your REST_FRAMEWORK settings add ExpiringTokenAuthentication as an Authentification class instead of TokenAuthentication:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
#'rest_framework.authentication.TokenAuthentication',
'yourmodule.authentication.ExpiringTokenAuthentication',
),
}

Thought I'd give a Django 2.0 answer using DRY. Somebody already built this out for us, google Django OAuth ToolKit. Available with pip, pip install django-oauth-toolkit. Instructions on adding the token ViewSets with routers: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html. It's similar to the official tutorial.
So basically OAuth1.0 was more yesterday's security which is what TokenAuthentication is. To get fancy expiring tokens, OAuth2.0 is all the rage these days. You get an AccessToken, RefreshToken, and scope variable to fine tune the permissions. You end up with creds like this:
{
"access_token": "<your_access_token>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<your_refresh_token>",
"scope": "read"
}

I've tried #odedfos answer but I had misleading error. Here is the same answer, fixed and with proper imports.
views.py
from django.utils import timezone
from rest_framework import status
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
class ObtainExpiringAuthToken(ObtainAuthToken):
def post(self, request):
serializer = self.serializer_class(data=request.DATA)
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.object['user'])
if not created:
# update the created time of the token to keep it valid
token.created = datetime.datetime.utcnow().replace(tzinfo=utc)
token.save()
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
authentication.py
from datetime import timedelta
from django.conf import settings
from django.utils import timezone
from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions
EXPIRE_HOURS = getattr(settings, 'REST_FRAMEWORK_TOKEN_EXPIRE_HOURS', 24)
class ExpiringTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
try:
token = self.model.objects.get(key=key)
except self.model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
if token.created < timezone.now() - timedelta(hours=EXPIRE_HOURS):
raise exceptions.AuthenticationFailed('Token has expired')
return (token.user, token)

The author asked
the question is, should the application renew / change the Token periodically and if yes how? Should it be the mobile app that requires the token to be renewed or the web-app should do it autonomously?
But all of the answers are writing about how to automatically change the token.
I think change token periodically by token is meaningless. The rest framework create a token that has 40 characters, if the attacker tests 1000 token every second, it requires 16**40/1000/3600/24/365=4.6*10^7 years to get the token. You should not worried that the attacker will test your token one by one. Even you changed your token, the probability of guess you token is the same.
If you are worried that maybe the attackers can get you token, so you change it periodically, than after the attacker get the token, he can also change you token, than the real user is kicked out.
What you should really do is to prevent tha attacker from getting your user's token, use https.
By the way, I'm just saying change token by token is meaningless, change token by username and password is sometimes meanful. Maybe the token is used in some http environment (you should always avoid this kind of situation) or some third party (in this case, you should create different kind of token, use oauth2) and when the user is doing some dangerous thing like changing binding mailbox or delete account, you should make sure you will not use the origin token anymore because it may has been revealed by the attacker using sniffer or tcpdump tools.

You can leverage http://getblimp.github.io/django-rest-framework-jwt
This library is able generate token that has an expiration date
To understand the difference between DRF default token and the token provide by the DRF take a look at:
How to make Django REST JWT Authentication scale with mulitple webservers?

It's a good practice to set an expiration mechanism on your app whether for mobile client or web client. There are two common solutions:
system expires token (after specific time) and user has to login again to gain new valid token.
system automatically expires old token (after specific time) and replaces it with new one (change token).
Common things in both of solutions:
Changes in settings.py
DEFAULT_AUTHENTICATION_CLASSES = [
# you replace right path of 'ExpiringTokenAuthentication' class
'accounts.token_utils.ExpiringTokenAuthentication'
]
TOKEN_EXPIRED_AFTER_MINUTES = 300
Create token_utils.py
from django.conf import settings
from datetime import timedelta
from django.conf import settings
from django.utils import timezone
from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token
from rest_framework.exceptions import AuthenticationFailed
def expires_in(token: Token):
elapsed_time = timezone.now() - token.created
return timedelta(minutes=settings.TOKEN_EXPIRED_AFTER_MINUTES) - elapsed_time
def is_token_expired(token):
return expires_in(token) < timedelta(seconds=0)
Changes in your views:
#api_view(['GET'])
#authentication_classes([ExpiringTokenAuthentication])
#permission_classes([IsAuthenticated])
def test(request):
...
return Response(response, stat_code)
If using option 1, add these lines to token_utils.py
def handle_token_expired(token):
Token.objects.filter(key=token).delete()
class ExpiringTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
try:
token = Token.objects.get(key=key)
except Token.DoesNotExist:
raise AuthenticationFailed("Invalid Token!")
if not token.user.is_active:
raise AuthenticationFailed("User inactive or deleted")
if is_token_expired(token):
handle_token_expired(token)
msg = "The token is expired!, user have to login again."
response = {"msg": msg}
raise AuthenticationFailed(response)
return token.user, token
If using option 2, add these lines to token_utils.py
def handle_token_expired(token):
is_expired = is_token_expired(token)
if is_expired:
token.delete()
token = Token.objects.create(user = token.user)
return is_expired, token
class ExpiringTokenAuthentication(TokenAuthentication):
"""
when token is expired, it will be removed
and new one will be created
"""
def authenticate_credentials(self, key):
try:
token = Token.objects.get(key = key)
except Token.DoesNotExist:
raise AuthenticationFailed("Invalid Token")
if not token.user.is_active:
raise AuthenticationFailed("User is not active")
is_expired, token = handle_token_expired(token)
if is_expired:
raise AuthenticationFailed("The Token is expired")
return (token.user, token)

If you notice that a token is like a session cookie then you could stick to the default lifetime of session cookies in Django: https://docs.djangoproject.com/en/1.4/ref/settings/#session-cookie-age.
I don't know if Django Rest Framework handles that automatically but you can always write a short script which filters out the outdated ones and marks them as expired.

Just thought I would add mine as this was helpful for me. I usually go with the JWT method but sometimes something like this is better. I updated the accepted answer for django 2.1 with proper imports..
authentication.py
from datetime import timedelta
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone
from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions
EXPIRE_HOURS = getattr(settings, 'REST_FRAMEWORK_TOKEN_EXPIRE_HOURS', 24)
class ExpiringTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
try:
token = self.get_model().objects.get(key=key)
except ObjectDoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
if token.created < timezone.now() - timedelta(hours=EXPIRE_HOURS):
raise exceptions.AuthenticationFailed('Token has expired')
return token.user, token
views.py
import datetime
from pytz import utc
from rest_framework import status
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.serializers import AuthTokenSerializer
class ObtainExpiringAuthToken(ObtainAuthToken):
def post(self, request, **kwargs):
serializer = AuthTokenSerializer(data=request.data)
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.validated_data['user'])
if not created:
# update the created time of the token to keep it valid
token.created = datetime.datetime.utcnow().replace(tzinfo=utc)
token.save()
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

just to keep adding to #odedfos answer, I think there have been some changes to the syntax so the code of ExpiringTokenAuthentication needs some adjusting:
from rest_framework.authentication import TokenAuthentication
from datetime import timedelta
from datetime import datetime
import datetime as dtime
import pytz
class ExpiringTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
# This is required for the time comparison
utc_now = datetime.now(dtime.timezone.utc)
utc_now = utc_now.replace(tzinfo=pytz.utc)
if token.created < utc_now - timedelta(hours=24):
raise exceptions.AuthenticationFailed('Token has expired')
return token.user, token
Also, don't forget to add it to DEFAULT_AUTHENTICATION_CLASSES instead of rest_framework.authentication.TokenAuthentication

If anyone wants to expire the token after certain time of inactivity, below answer would help. I am tweaking one of the answers given here. I have added comments to the code I added
from rest_framework.authentication import TokenAuthentication
from datetime import timedelta
from datetime import datetime
import datetime as dtime
import pytz
class ExpiringTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
# This is required for the time comparison
utc_now = datetime.now(dtime.timezone.utc)
utc_now = utc_now.replace(tzinfo=pytz.utc)
if token.created < utc_now - timedelta(minutes=15): # TOKEN WILL EXPIRE AFTER 15 MINUTES OF INACTIVITY
token.delete() # ADDED THIS LINE SO THAT EXPIRED TOKEN IS DELETED
raise exceptions.AuthenticationFailed('Token has expired')
else:
token.created = utc_now #THIS WILL SET THE token.created TO CURRENT TIME WITH EVERY REQUEST
token.save() #SAVE THE TOKEN
return token.user, token

Related

DRF simpleJWT - Is there anyway to fetch user's data from an access-token with no access to the 'request' param?

I'm trying to write a custom authentication middleware for Django channels since I'm using JWT for my app's authentication scheme. I'm using the method that is mentioned in this article which basically gets user's token in the first request that is made to the websockets and then, in the receive method of the consumers.py file, fetches user's data based on that and then pours it in the self.scope['user'] (can't make use of the token_auth.py method because the UI app is separate..). Now since I have NO ACCESS to the request param that is usually being used in the views.py files to get the user's data, is there anyway to get the user's data out of an access token alone??
Hello #Jalal try this.
from channels.auth import AuthMiddlewareStack
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from rest_framework_simplejwt.backends import TokenBackend
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
headers = dict(scope['headers'])
if b'authorization' in headers:
try:
token_name, token_key = headers[b'authorization'].decode().split()
if token_name == 'Token':
print(token_key)
valid_data = TokenBackend(algorithm='HS256').decode(token_key,verify=False)
print(valid_data)
scope['user_id'] = valid_data['user_id']
close_old_connections()
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
return self.inner(scope)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
Here valid_data return this:
{'token_type': 'access', 'exp': 1627994463, 'jti':
'192275bd7fd649758838ave1899e1863', 'user_id': 2}
If you want to fetch more data(like:Username,email,etc) through access token then use Customizing token claims:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token['username'] = user.username
token['email'] = user.email
# ...
return token
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer

Django Channels ERROR: TypeError: object.__init__() takes exactly one argument (the instance to initialize)

I am using a custom JWT Authentication Middleware for verifying of JWT.
import jwt
from urllib.parse import parse_qs
from channels.db import database_sync_to_async
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.conf import settings
from rest_framework_simplejwt.tokens import AccessToken
#database_sync_to_async
def get_user(user_id):
User = get_user_model()
try:
user = User.objects.get(id=user_id)
return user
except User.DoesNotExist:
return AnonymousUser()
class TokenAuthMiddleware:
"""
Custom middleware (insecure) that takes user IDs from the query string.
"""
def __init__(self, inner):
# Store the ASGI application we were passed
self.inner = inner
async def __call__(self, scope, receive, send):
# Look up user from query string (you should also do things like
# checking if it is a valid user ID, or if scope["user"] is already
# populated).
token = parse_qs(scope["query_string"].decode())["token"][0]
AccessToken(token).verify()
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
scope["user"] = await get_user(int(payload["user_id"]))
return await self.inner(dict(scope), receive, send)
I am getting the error TypeError: object.__init__() takes exactly one argument (the instance to initialize)
I followed the official docs
Can anyone guide me where is exactly the issue?
For anyone facing the same issue, the problem is not actually in the Middleware. For my case, the issue was in asgi.py. I had forgot to call the .as_asgi() method on the consumer.
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
# Just HTTP for now. (We can add other protocols later.)
"websocket": TokenAuthMiddlewareStack(
URLRouter([
path("chat/", ChatConsumer.as_asgi()),
])
)
}
)
Note the as_asgi() method on ChatConsumer. I missed that which eventually led me to think that the issue was in my custom middleware.

Django Expire token Authentication

I am working on expiring a django Token for this I am following this https://medium.com/#yerkebulan199/django-rest-framework-drf-token-authentication-with-expires-in-a05c1d2b7e05
and my accounts/authentication.py file is like
from rest_framework.authentication import TokenAuthentication
from rest_framework.authtoken.models import Token
from rest_framework.exceptions import AuthenticationFailed
from datetime import timedelta
from django.utils import timezone
from django.conf import settings
# this return left time
def expires_in(token):
time_elapsed = timezone.now() - token.created
left_time = timedelta(seconds=settings.TOKEN_EXPIRED_AFTER_SECONDS) - time_elapsed
return left_time
# token checker if token expired or not
def is_token_expired(token):
return expires_in(token) < timedelta(seconds=0)
# if token is expired new token will be established
# If token is expired then it will be removed
# and new one with different key will be created
def token_expire_handler(token):
is_expired = is_token_expired(token)
if is_expired:
token.delete()
token = Token.objects.create(user=token.user)
return is_expired, token
# ________________________________________________
# DEFAULT_AUTHENTICATION_CLASSES
class ExpiringTokenAuthentication(TokenAuthentication):
"""
If token is expired then it will be removed
and new one with different key will be created
"""
def authenticate_credentials(self, key):
try:
token = Token.objects.get(key=key)
except Token.DoesNotExist:
raise AuthenticationFailed("Invalid Token")
if not token.user.is_active:
raise AuthenticationFailed("User is not active")
is_expired, token = token_expire_handler(token)
if is_expired:
raise AuthenticationFailed("The Token is expired")
return (token.user, token)
and I have added in settings.py like this
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'accounts.authentication.ExpiringTokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
TOKEN_EXPIRED_AFTER_SECONDS = 10
Still token is not renewing , I have checked this authentication class is not triggering .
try it in side of expires_in function
time_elapsed = token.created - timezone.now()
cuz time_elapsed should be positive to get left_time

django.db.utils.IntegrityError: UNIQUE constraint failed: authtoken_token.user_id

I'm new in Django rest framework and I'm trying do a unit test using Token for my API, but it kept throwing IntegrityError. I've researched many blogs to find the solution but couldn't find. Please help me solve this. Thanks in advance.
Here is the code that I've tried
from django.contrib.auth.models import User
from rest_framework.test import APITestCase, APIRequestFactory, force_authenticate
from rest_framework.authtoken.models import Token
from myapp.api.views import UserViewSet
class UserTestCase(APITestCase):
def setUp(self):
self.superuser = User.objects.create_user(username='superuser', email='uid.sawyer#gmail.com',
password='superuser',
is_staff=True)
self.factory = APIRequestFactory()
self.token = Token.objects.create(user=self.superuser)
self.token.save()
def test_list(self):
request = self.factory.get('/api/users/')
force_authenticate(request, user=self.superuser, token=self.token)
response = UserViewSet.as_view({'get': 'list'})(request)
self.assertEqual(response.status_code, 200)
unique constraint failed: authtoken_token.user_id
This error is coming because token already exists for this user in database. So when it tries to create new token with same user_id it gives an error
If you look at the admin token already exists for users
This is referenced from https://excellencetechnologies.in/blog/django-rest-api-authentication-part5/:
Do a try-except function:
class UserAuth(APIView):
# Manager User Login and other things
def post(self, request):
# login
user = authenticate(
username=request.data.get("username"),
password=request.data.get("password")
)
if user is not None:
# A backend authenticated the credentials
try:
token = Token.objects.get(user_id=user.id)
except Token.DoesNotExist:
token = Token.objects.create(user=user)
return Response(token.key)
else:
# No backend authenticated the credentials
return Response([], status=status.HTTP_401_UNAUTHORIZED)
Finally, I got it. I didn't notice that I had created Token in signals wherein each User creation Token was created. Thanks #Willem Van Onsem

Modifying jwt access token expiry time in django using simplejwt module

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.utils import datetime_to_epoch
SUPERUSER_LIFETIME = datetime.timedelta(minutes=1)
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super(MyTokenObtainPairSerializer, cls).get_token(user)
token['name'] = user.username
token['user_id'] = user.id
if user.is_superuser:
#token.set_exp(from_time=starttime,lifetime=SUPERUSER_LIFETIME)
token.payload['exp'] = datetime_to_epoch(token.current_time + SUPERUSER_LIFETIME)
return token
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
i have tried this code (followed this link: How can we assign different expiry time to different users in jwt tokens in django ). This code updates the expiry time of refresh token but i want to update expiry time of access token in django using simplejwt module. any suggestions please.
I just made a quick look to simplejwt github's page and you can customize some settings in your settings.py file;
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
}
Updated Answer Based On Comment
thanks for response . but i want set globally jwt expiry time and later based on role , i want to override that expiry time . how is it possible??
As you say, you have to override default token generator method. But how?
First, create your own token obtain view that inherited from TokenObtainPairView and your own token obtain serializer that inherited from TokenObtainPairSerializer. After that, you can see that validate method create access and refresh tokens, so also you must override that method if you want to create token based on user role etc. After these steps you also have to change your urls.py.
Example;
import datetime
from django.utils.six import text_type
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
SUPERUSER_LIFETIME = datetime.timedelta(minutes=1)
class MyTokenObtainSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super(TokenObtainPairSerializer, self).validate(attrs)
refresh = self.get_token(self.user)
data['refresh'] = text_type(refresh)
if self.user.is_superuser:
new_token = refresh.access_token
new_token.set_exp(lifetime=SUPERUSER_LIFETIME)
data['access'] = text_type(new_token)
else:
data['access'] = text_type(refresh.access_token)
return data
class MyTokenObtainView(TokenObtainPairView):
serializer_class = MyTokenObtainSerializer
urls.py
urlpatterns = [
path('api/token/', MyTokenObtainView.as_view(), name='token_obtain_pair')
]