From bfc929b4be94d871a33b1371664e97b1d5ff9dac Mon Sep 17 00:00:00 2001 From: valentin Date: Sun, 8 Mar 2020 19:03:55 +0100 Subject: [PATCH 1/3] Use the AUTH_COOKIE_SAMESITE settings to set the refresh samesite attribute --- rest_framework_simplejwt/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework_simplejwt/views.py b/rest_framework_simplejwt/views.py index 351bd5977..fbf8a23d9 100644 --- a/rest_framework_simplejwt/views.py +++ b/rest_framework_simplejwt/views.py @@ -91,7 +91,7 @@ def set_auth_cookies(self, response, data): path=reverse(self.token_refresh_view_name), secure=api_settings.AUTH_COOKIE_SECURE or None, httponly=True, - samesite='Strict', + samesite=api_settings.AUTH_COOKIE_SAMESITE, ) return response From d6ff1f02571294398379d9dd96d3cf316fb23b70 Mon Sep 17 00:00:00 2001 From: valentin Date: Sat, 14 Mar 2020 12:50:26 +0100 Subject: [PATCH 2/3] Add the support of 'none' in the samesite cookie attribute --- rest_framework_simplejwt/views.py | 55 +++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/rest_framework_simplejwt/views.py b/rest_framework_simplejwt/views.py index fbf8a23d9..65abb02e6 100644 --- a/rest_framework_simplejwt/views.py +++ b/rest_framework_simplejwt/views.py @@ -1,10 +1,13 @@ -from datetime import datetime +import time +from datetime import datetime, timedelta from django.middleware import csrf from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone +from django.utils.http import http_date from rest_framework import generics, status from rest_framework.exceptions import NotAuthenticated -from rest_framework.response import Response +from rest_framework.response import Response as R from rest_framework.reverse import reverse from rest_framework.views import APIView @@ -14,6 +17,54 @@ from .authentication import AUTH_HEADER_TYPES from .exceptions import InvalidToken, TokenError +#Need to set samesite to None. (Available in django 3.1) +class Response(R): + + def set_cookie(self, key, value='', max_age=None, expires=None, path='/', + domain=None, secure=False, httponly=False, samesite=None): + """ + Set a cookie. + ``expires`` can be: + - a string in the correct format, + - a naive ``datetime.datetime`` object in UTC, + - an aware ``datetime.datetime`` object in any time zone. + If it is a ``datetime.datetime`` object then calculate ``max_age``. + """ + self.cookies[key] = value + if expires is not None: + if isinstance(expires, datetime): + if timezone.is_aware(expires): + expires = timezone.make_naive(expires, timezone.utc) + delta = expires - expires.utcnow() + # Add one second so the date matches exactly (a fraction of + # time gets lost between converting to a timedelta and + # then the date string). + delta = delta + timedelta(seconds=1) + # Just set max_age - the max_age logic will set expires. + expires = None + max_age = max(0, delta.days * 86400 + delta.seconds) + else: + self.cookies[key]['expires'] = expires + else: + self.cookies[key]['expires'] = '' + if max_age is not None: + self.cookies[key]['max-age'] = max_age + # IE requires expires, so set it if hasn't been already. + if not expires: + self.cookies[key]['expires'] = http_date(time.time() + max_age) + if path is not None: + self.cookies[key]['path'] = path + if domain is not None: + self.cookies[key]['domain'] = domain + if secure: + self.cookies[key]['secure'] = True + if httponly: + self.cookies[key]['httponly'] = True + if samesite: + if samesite.lower() not in ('lax', 'none', 'strict'): + raise ValueError('samesite must be "lax", "none", or "strict".') + self.cookies[key]['samesite'] = samesite + class TokenViewBase(generics.GenericAPIView): permission_classes = () From e8038701a3ad0e2a88fd96d069bb4b8fdf8af3b1 Mon Sep 17 00:00:00 2001 From: valentin Date: Thu, 17 Sep 2020 22:53:39 +0200 Subject: [PATCH 3/3] Add samesite attribute in the delete_cookie call --- rest_framework_simplejwt/views.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rest_framework_simplejwt/views.py b/rest_framework_simplejwt/views.py index 65abb02e6..abb544f53 100644 --- a/rest_framework_simplejwt/views.py +++ b/rest_framework_simplejwt/views.py @@ -18,6 +18,7 @@ from .exceptions import InvalidToken, TokenError #Need to set samesite to None. (Available in django 3.1) +# set secure to True with None class Response(R): def set_cookie(self, key, value='', max_age=None, expires=None, path='/', @@ -56,7 +57,7 @@ def set_cookie(self, key, value='', max_age=None, expires=None, path='/', self.cookies[key]['path'] = path if domain is not None: self.cookies[key]['domain'] = domain - if secure: + if secure or samesite.lower() == 'none': self.cookies[key]['secure'] = True if httponly: self.cookies[key]['httponly'] = True @@ -256,12 +257,14 @@ def delete_auth_cookies(self, response): response.delete_cookie( api_settings.AUTH_COOKIE, domain=api_settings.AUTH_COOKIE_DOMAIN, - path=api_settings.AUTH_COOKIE_PATH + path=api_settings.AUTH_COOKIE_PATH, + samesite=api_settings.AUTH_COOKIE_SAMESITE ) response.delete_cookie( '{}_refresh'.format(api_settings.AUTH_COOKIE), domain=None, path=reverse(self.token_refresh_view_name), + samesite=api_settings.AUTH_COOKIE_SAMESITE )