Search code examples
djangocookies

Marking a Cookie as Partitioned in Django


This is my view:

@ensure_csrf_cookie
@require_http_methods(['GET'])
def set_csrf_token(request):
    return JsonResponse({'message': 'Set CSRF cookie'})

I set this cookie in a Cross-Origin context so I also have to enable in my settings.py:

CSRF_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SECURE = True

However, my browser (FireFox and Chrome) both require the use of Partitioned cookies since I'm sending this cookie with "None; Secure" which the browser interprets as a 3rd-party server or Cross-Origin

Yet, Django appears to lack a setting like CSRF_COOKIE_PARTITIONED which would allow me to mark my cookie as Partitioned

So I ask what would be an elegant solution to setting my cookie as Partitioned?

Edit: the Partitioned attribute is not actually required but more so recommended. It sounds like both browsers are moving towards this attribute being required though


Solution

  • There are no elegant solutions yet.

    The work to get support for Partitioned into Python stblib is complete, but they missed the 3.13 release window - so as of yet, they are waiting for a new window to merge upstream.

    Django maintainers seemingly don't want to do too much work for this until upstream support in stblib is confirmed, either. However - this thread (specifically the comment this URL leads to) contains an alleged workaround for the CSRF token, using a custom Django middleware:

    # Author: Terence Honles
    ...
    from http import cookies
    
    ...
    cookies.Morsel._flags.add("partitioned")
    cookies.Morsel._reserved.setdefault("partitioned", "Partitioned")
    
    class CookiePartitioningMiddleware(MiddlewareMixin):
        def process_response(
            self, request: HttpRequest, response: HttpResponseBase
        ) -> HttpResponseBase:
            for name in (
                getattr(settings, f"{prefix}_COOKIE_NAME")
                for prefix in ("CSRF", "SESSION", "LANGUAGE")
                if getattr(settings, f"{prefix}_COOKIE_SECURE")
            ):
                if cookie := response.cookies.get(name):
                    cookie["Partitioned"] = True
    
            return response
    

    If this type of approach doesn't work, you can try patching http.cookies yourself using the PR. Comments in the pull request also include a rough implementation draft.