djangodjango-rest-frameworkdjango-rest-framework-jwtdjango-rest-framework-simplejwt

How to store JWT tokens in HttpOnly cookies with DRF djangorestframework-simplejwt package?


I've been using djangorestframework-simplejwt for a while and now I want to store the JWT in the cookies (instead of localstorage or front-end states) so that every request that the client makes, contains the token.

So did some research on it and the most relevant result I found was this stackoverflow question, in which the author is using djangorestframework-jwt package which has a pre-configured setting for cookies called JWT_AUTH_COOKIE. So figured switching to that package but then ended up finding out that the package is pretty much dead.

Although there is a fork for the djangorestframework-jwt that is recommended to use instead, I was wondering is there anyway to set the JWTs in HttpOnly cookies with the djagnorestframework_simplejwt itself?


Solution

  • With httponly cookie flag and CSRF protection follow this code.

    Both side very useful in mobile app and webapp..

    urls.py:

    ...
    path('login/',LoginView.as_view(),name = "login"),
    ...
    

    view.py:

    from rest_framework_simplejwt.tokens import RefreshToken
    from django.middleware import csrf
    
    def get_tokens_for_user(user):
        refresh = RefreshToken.for_user(user)
            
        return {
            'refresh': str(refresh),
            'access': str(refresh.access_token),
        }
    
    class LoginView(APIView):
        def post(self, request, format=None):
            data = request.data
            response = Response()        
            username = data.get('username', None)
            password = data.get('password', None)
            user = authenticate(username=username, password=password)
            if user is not None:
                if user.is_active:
                    data = get_tokens_for_user(user)
                    response.set_cookie(
                                        key = settings.SIMPLE_JWT['AUTH_COOKIE'], 
                                        value = data["access"],
                                        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_SAMESITE']
                                            )
                    csrf.get_token(request)
                    email_template = render_to_string('login_success.html',{"username":user.username})    
                    login = EmailMultiAlternatives(
                        "Successfully Login", 
                        "Successfully Login",
                        settings.EMAIL_HOST_USER, 
                        [user.email],
                    )
                    login.attach_alternative(email_template, 'text/html')
                    login.send()
                    response.data = {"Success" : "Login successfully","data":data}
                    
                    return response
                else:
                    return Response({"No active" : "This account is not active!!"},status=status.HTTP_404_NOT_FOUND)
            else:
                return Response({"Invalid" : "Invalid username or password!!"},status=status.HTTP_404_NOT_FOUND)
    

    authenticate.py:

    from rest_framework_simplejwt.authentication import JWTAuthentication
    from django.conf import settings
    
    from rest_framework.authentication import CSRFCheck
    from rest_framework import exceptions
    
    def enforce_csrf(request):
        """
        Enforce CSRF validation.
        """
        check = CSRFCheck()
        # populates request.META['CSRF_COOKIE'], which is used in process_view()
        check.process_request(request)
        reason = check.process_view(request, None, (), {})
        if reason:
            # CSRF failed, bail with explicit error message
            raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)
    
    class CustomAuthentication(JWTAuthentication):
        
        def authenticate(self, request):
            header = self.get_header(request)
            
            if header is None:
                raw_token = request.COOKIES.get(settings.SIMPLE_JWT['AUTH_COOKIE']) or None
            else:
                raw_token = self.get_raw_token(header)
            if raw_token is None:
                return None
    
            validated_token = self.get_validated_token(raw_token)
            enforce_csrf(request)
            return self.get_user(validated_token), validated_token
    

    settings.py:

    ....
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'authentication.authenticate.CustomAuthentication',
        ),
    }
    
    SIMPLE_JWT = {
    .....
    'AUTH_COOKIE': 'access_token',  # 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_SAMESITE': 'Lax',  # Whether to set the flag restricting cookie leaks on cross-site requests.
                                    # This can be 'Lax', 'Strict', or None to disable the flag.
    }
    

    --------- OR ------------

    By using middleware.py:

    How to authenticate by using middleware

    Must :

    withCredentials is True from both side..

    Any doubt please comment..