Search code examples
pythondjangodjango-rest-frameworkdjango-rest-framework-simplejwtcookie-httponly

User logout after the first access token expires in django


The user is logout after the first access token expires.

How do I automatically create and set a new access token after it has expired?


Solution

  • Just customize the TokenRefreshView method

    views

    In the program you are building, go to the views section and add the following class.

    class CustomTokenRefreshView(TokenRefreshView):
    
        def post(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
    
            try:
                serializer.is_valid(raise_exception=True)
            except TokenError as e:
                raise InvalidToken(e.args[0])
    
            # set Refresh and Access token in browser with Httponly cookie.
            response = Response(serializer.validated_data, status=status.HTTP_200_OK)
            response.set_cookie(
                key=settings.SIMPLE_JWT['AUTH_COOKIE_REFRESH'],
                value=serializer.validated_data["refresh"],
                domain=settings.SIMPLE_JWT['AUTH_COOKIE_DOMAIN'],
                path=settings.SIMPLE_JWT['AUTH_COOKIE_PATH'],
                expires=settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
                secure=settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
                httponly=settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
                samesite=settings.SIMPLE_JWT['AUTH_COOKIE_SAME_SITE']
            )
            response.set_cookie(
                key=settings.SIMPLE_JWT['AUTH_COOKIE_ACCESS'],
                value=serializer.validated_data["access"],
                domain=settings.SIMPLE_JWT['AUTH_COOKIE_DOMAIN'],
                path=settings.SIMPLE_JWT['AUTH_COOKIE_PATH'],
                expires=settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
                secure=settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
                httponly=settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
                samesite=settings.SIMPLE_JWT['AUTH_COOKIE_SAME_SITE']
            )
            return response
    

    URLs

    for class-based views

    views.CustomTokenRefreshView.as_view()

        path('token/refresh/', views.CustomTokenRefreshView.as_view(), name='token_refresh'),
        path('token/verify/', TokenVerifyView.as_view(), name='token_refresh'),
    

    Middleware

    Of course, you have to add the following as well.

    Create a file called middleware.py in the program you are building, then add these two custom classes.

    class MoveJWTCookieIntoTheBody(MiddlewareMixin):
        """
        for Django Rest Framework JWT's POST "/token-refresh" endpoint --- check for a 'token' in the request.COOKIES
        and if, add it to the body payload.
        """
    
        def __init__(self, get_response):
            super().__init__(get_response)
            self.get_response = get_response
    
        def __call__(self, request):
            response = self.get_response(request)
            return response
    
        def process_view(self, request, view_func, *view_args, **view_kwargs):
            if request.path == '/auth/token/verify/' and settings.SIMPLE_JWT['AUTH_COOKIE_ACCESS'] in request.COOKIES:
    
                if request.body != b'':
                    data = json.loads(request.body)
                    data['token'] = request.COOKIES[settings.SIMPLE_JWT['AUTH_COOKIE_ACCESS']]
                    request._body = json.dumps(data).encode('utf-8')
    
    
    class MoveJWTRefreshCookieIntoTheBody(MiddlewareMixin):
        """
        for Django Rest Framework JWT's POST "/token-refresh" endpoint --- check for a 'token' in the request.COOKIES
        and if, add it to the body payload.
        """
    
        def __init__(self, get_response):
            super().__init__(get_response)
            self.get_response = get_response
    
        def __call__(self, request):
            response = self.get_response(request)
            return response
    
        def process_view(self, request, view_func, *view_args, **view_kwargs):
            if request.path == '/auth/token/refresh/' and settings.SIMPLE_JWT['AUTH_COOKIE_REFRESH'] in request.COOKIES:
    
                if request.body != b'':
                    data = json.loads(request.body)
                    data['refresh'] = request.COOKIES[settings.SIMPLE_JWT['AUTH_COOKIE_REFRESH']]
                    request._body = json.dumps(data).encode('utf-8')
               
    

    Settings

    Then you have to add these in the settings.

    import datetime
    
    ...
    
    MIDDLEWARE = [
        ...
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    
        'accounts.middleware.MoveJWTCookieIntoTheBody',
        'accounts.middleware.MoveJWTRefreshCookieIntoTheBody'
    ]
    
    ...
    
    SIMPLE_JWT = {
        'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
        'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
        'ROTATE_REFRESH_TOKENS': False,
        'BLACKLIST_AFTER_ROTATION': False,
        'UPDATE_LAST_LOGIN': False,
    
        'ALGORITHM': 'HS256',
        'SIGNING_KEY': settings.SECRET_KEY,
        'VERIFYING_KEY': None,
        'AUDIENCE': None,
        'ISSUER': None,
        'JWK_URL': None,
        'LEEWAY': 0,
    
        'AUTH_HEADER_TYPES': ('Bearer',),
        'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
        'USER_ID_FIELD': 'id',
        'USER_ID_CLAIM': 'user_id',
        'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
    
        'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
        'TOKEN_TYPE_CLAIM': 'token_type',
    
        'JTI_CLAIM': 'jti',
    
        'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
        'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
        'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
    
        # Custom_SIMPLE_JWT
        'AUTH_COOKIE': 'token',  # Cookie name. Enables cookies if value is set.
        'AUTH_COOKIE_ACCESS': 'access',  # Cookie name. Enables cookies if value is set.
        'AUTH_COOKIE_REFRESH': 'refresh',  # Cookie name. Enables cookies if value is set.
        'AUTH_COOKIE_DOMAIN': None,  # A string like "example.com", or None for standard domain cookie.
        'AUTH_COOKIE_SECURE': False,  # Whether the auth cookies should be secure (https:// only).
        'AUTH_COOKIE_HTTP_ONLY': True,  # Http only cookie flag.It's not fetch by javascript.
        'AUTH_COOKIE_PATH': '/',  # The path of the auth cookie.
        'AUTH_COOKIE_SAME_SITE': 'Lax',  # Whether to set the flag restricting cookie leaks on cross-site requests.
        # This can be 'Lax', 'Strict', or None to disable the flag.
    }
    

    The only problem is that it gives a new access_token whenever it is reloaded.

    What do you think should be done?