How to have different expiry times for Web and Mobile Apps in Django simple jwt?

Viewed 483

I am currently using Django Rest Framework to serve a React JS application, but recently, we are adding support for a React Native application as well.

Now, as I use Django Simple jwt, here's the code for the expiry of the refresh and access tokens:

settings.py

ACCESS_TOKEN_LIFETIME = datetime.timedelta(hours=2)
REFRESH_TOKEN_LIFETIME = datetime.timedelta(days=3)

SIMPLE_JWT = {

    'ACCESS_TOKEN_LIFETIME': ACCESS_TOKEN_LIFETIME,
    'REFRESH_TOKEN_LIFETIME': REFRESH_TOKEN_LIFETIME,
    ...

}

While this works really well on web, I do not want the phone app users to have to get logged out automatically every 3 days.

Is there a way to alter the refresh token lifetime based on the device that is asking for the tokens?

If yes, how?

2 Answers

Actually, I had the same issue. but I came up with an idea and I know there Should be some better solution to this. What I did is I set the refresh token lifetime to the max time which I wanted e.g, 90 days max. Then handled the expiration in frontends in different manners, in ReactJs frontend besides saving the JWT credentials I saved a new record as "LOGIN_TIMESTAMP" which stores the exact time when the refresh token was given by the Django backend at the beginning of the authentication scenario, to be exact it is on login action time. And each time the user is about to do something to the react state, ReactJS checks if it's less/equal to a specific amount of time which in my case is less than 86,400 seconds a.k.a 24 hours if so it carries on working as it is supposed to do. If it isn't less than that specific time it sends a log-out request to the Django backend which causes to move the current token to the Blacklisted Tokens model and then the user will be redirected to the login page. In other frontend platforms e.g, Swift, React Native, Kotlin and etcetera, I rather do nothing and let the refresh token lifetime which was 90 days in my case do its job, and after refresh token expiration the user will be guided to Login View.

api/views.py
class BlackListTokenView(APIView):
    def post(self, request):
        try: 
            refresh_token = request.data["refresh_token"]
            token = RefreshToken(refresh_token)
            token.blacklist()
        except Exception as e:
            return Response({}, status = status.HTTP_400_BAD_REQUEST)
        return Response({}, status = status.HTTP_200_OK)
api/urls.py
urlpatterns = [
path('user/logout/', BlackListTokenView.as_view(), name="blacklist")]

UPDATE:

During the last recent few weeks once again I was dealing with the same problem. Thank god finally I came up with a better solution that handles everything on Django's Back-End side. I've just released it as a package in PyPi and in a GIT Repo. The whole tutorial is also included. It is based on djangorestframework-simplejwt so you still need this plugin in your project.

You can install it:

$ pip install simplejwt-multisessions

Briefly, simplejwt-multisessions not only supports two different refresh lifetimes but brings more features to your Django project e.g., managing the number of active sessions of different categories of lifetimes like extending lifetime policies and etc.

You should check if the user is using a mobile, for that you have several libraries that satisfy this functionality. For example:

django-user_agents

from django_user_agents.utils import get_user_agent

def my_view(request):
    user_agent = get_user_agent(request)
    if user_agent.is_mobile:
        # Do stuff here...
    elif user_agent.is_tablet:
        # Do other stuff...

I don't know which version of django you have but keep in mind that its latest update was for version 2.2. If this package doesn't work for your version you can use its dependency directly python-user-agents.

from user_agents import parse

def my_view(request):
    ua_string = request.META['HTTP_USER_AGENT']
    user_agent = parse(ua_string)
    if user_agent.is_mobile:
        # Do stuff here...
    elif user_agent.is_tablet:
        # Do other stuff...

and finally you should add a mobile config

ACCESS_TOKEN_LIFETIME = datetime.timedelta(hours=2)
REFRESH_TOKEN_LIFETIME = datetime.timedelta(days=3)
ACCESS_TOKEN_LIFETIME_MOBILE = datetime.timedelta(hours=n)
REFRESH_TOKEN_LIFETIME_MOBILE = datetime.timedelta(days=n)

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': ACCESS_TOKEN_LIFETIME,
    'REFRESH_TOKEN_LIFETIME': REFRESH_TOKEN_LIFETIME,
    'ACCESS_TOKEN_LIFETIME_MOBILE': ACCESS_TOKEN_LIFETIME_MOBILE,
    'REFRESH_TOKEN_LIFETIME_MOBILE': REFRESH_TOKEN_LIFETIME_MOBILE,
    ...
}
Related