pythondjangodjango-rest-frameworkjwtdj-rest-auth

Refresh token being passed in response body in registration but not login in Django


I am using the Django REST Framework with dj-rest-auth and django-allauth for authentication and authorisation with JWTs. From what I have heard, using HTTP-only cookies to store the tokens is a secure approach where the access token is passed in the response body but the refresh token is passed to the browser as a cookie.

The default (straight from dj_rest_auth.registration.views) login endpoint works this way, returning the access token in the response body and returning the access and refresh tokens as HTTP-only cookies as well as a csrftoken and sessionid. However, the default signup endpoint doesn't exhibit the same behaviour, it returns both access and refresh tokens in the response body and returns neither as a cookie, but does return these cookies: messages, csrftoken, sessionid. Does anyone know why this might be happening and how I could get registration to behave the same way as login? Here are the relevant parts of my settings.py:

REST_AUTH = {
    "USE_JWT": True,
    "JWT_AUTH_COOKIE": "access-tkk",
    "JWT_AUTH_REFRESH_COOKIE": "refresh-tkk",
    # "JWT_AUTH_SECURE": True,  # Use secure cookies in production for HTTPS only
    "JWT_AUTH_HTTPONLY": True,  # Secure HTTP-only cookies
    "REGISTER_SERIALIZER": "authentication.serializers.CustomRegisterSerializer",
    "USER_DETAILS_SERIALIZER": "authentication.serializers.CustomUserDetailsSerializer",
}


SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),  # Short-lived access token
    "REFRESH_TOKEN_LIFETIME": timedelta(days=14),  # Longer-lived refresh token
    "ROTATE_REFRESH_TOKENS": True,  # Issue new refresh token on refresh
    "BLACKLIST_AFTER_ROTATION": True,  # Blacklist old refresh tokens
    "UPDATE_LAST_LOGIN": True,
    "ALGORITHM": "HS256",
    "AUTH_HEADER_TYPES": ("Bearer",),
    "AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
}

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "dj_rest_auth.jwt_auth.JWTCookieAuthentication",
        "rest_framework.authentication.TokenAuthentication",
    ),
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
}

Solution

  • Problem:
    dj-rest-auth login sets JWTs in HTTP-only cookies, but registration does not — it only returns tokens in the response body.

    Cause:
    RegisterView doesn’t include the logic to set cookies like LoginView does.

    To Fix: - To set JWT cookies on registration (just like login), extend RegisterView and manually set the cookies.

    Create a custom RegisterView:

    # views.py:
    
    from dj_rest_auth.registration.views import RegisterView
    from dj_rest_auth.jwt_auth import set_jwt_cookies
    from rest_framework import status
    
    class CustomRegisterView(RegisterView):
        def create(self, request, *args, **kwargs):
            response = super().create(request, *args, **kwargs)
            if response.status_code == status.HTTP_201_CREATED:
                access = response.data.get("access")
                refresh = response.data.get("refresh")
                if access and refresh:
                    set_jwt_cookies(response, access, refresh)
            return response
    
    # urls.py:
    
    from .views import CustomRegisterView
    
    urlpatterns = [
        path("auth/register/", CustomRegisterView.as_view(), name="custom_register"),
    ]