Skip to content

Commit 16ee577

Browse files
authored
Remove session fallback cookie (#11673)
We set the fallback cookie to work around some old browsers rejecting cookies with `SameSite=None` https://www.chromium.org/updates/same-site/incompatible-clients/. That note is from 2019, I think we should be fine without supporting those old browsers, and browsers from supported devices are already patched to support that cookie. This is adding an additional cookie that takes some space.
1 parent 5d651d3 commit 16ee577

File tree

2 files changed

+4
-136
lines changed

2 files changed

+4
-136
lines changed

readthedocs/core/middleware.py

Lines changed: 4 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
1-
import time
2-
31
import structlog
42
from django.conf import settings
5-
from django.contrib.sessions.backends.base import SessionBase, UpdateError
3+
from django.contrib.sessions.backends.base import SessionBase
64
from django.contrib.sessions.middleware import SessionMiddleware
7-
from django.core.exceptions import (
8-
ImproperlyConfigured,
9-
MiddlewareNotUsed,
10-
SuspiciousOperation,
11-
)
5+
from django.core.exceptions import ImproperlyConfigured, MiddlewareNotUsed
126
from django.http import HttpResponse
13-
from django.utils.cache import patch_vary_headers
14-
from django.utils.http import http_date
157

168
log = structlog.get_logger(__name__)
179

@@ -22,17 +14,6 @@ class ReadTheDocsSessionMiddleware(SessionMiddleware):
2214
An overridden session middleware with a few changes.
2315
2416
- Doesn't create a session on logged out doc views.
25-
- Uses a fallback cookie for browsers that reject SameSite=None cookies
26-
- Modifies Django's behavior of treating SESSION_COOKIE_SAMESITE=None
27-
to mean SameSite unset and instead makes it mean SameSite=None
28-
29-
This overrides and replaces Django's built-in SessionMiddleware completely.
30-
Much of this middleware is duplicated from Django 2.2's SessionMiddleware.
31-
32-
In Django 3.1, Django will fully support SameSite=None cookies.
33-
However, we may still need this middleware to support browsers that reject
34-
SameSite=None cookies.
35-
https://www.chromium.org/updates/same-site/incompatible-clients
3617
"""
3718

3819
# Don't set a session cookie on these URLs unless the cookie is already set
@@ -42,125 +23,27 @@ class ReadTheDocsSessionMiddleware(SessionMiddleware):
4223
"/sustainability/click",
4324
]
4425

45-
# This is a fallback cookie for the regular session cookie
46-
# It is only used by clients that reject cookies with `SameSite=None`
47-
cookie_name_fallback = f"{settings.SESSION_COOKIE_NAME}-samesiteunset"
48-
4926
def process_request(self, request):
5027
for url in self.IGNORE_URLS:
5128
if (
5229
request.path_info.startswith(url)
5330
and settings.SESSION_COOKIE_NAME not in request.COOKIES
54-
and self.cookie_name_fallback not in request.COOKIES
5531
):
5632
# Hack request.session otherwise the Authentication middleware complains.
5733
request.session = SessionBase() # create an empty session
5834
return
5935

60-
if settings.SESSION_COOKIE_SAMESITE == "None":
61-
if settings.SESSION_COOKIE_NAME in request.COOKIES:
62-
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
63-
else:
64-
session_key = request.COOKIES.get(self.cookie_name_fallback)
65-
66-
request.session = self.SessionStore(session_key)
67-
else:
68-
super().process_request(request)
36+
super().process_request(request)
6937

7038
def process_response(self, request, response):
7139
for url in self.IGNORE_URLS:
7240
if (
7341
request.path_info.startswith(url)
7442
and settings.SESSION_COOKIE_NAME not in request.COOKIES
75-
and self.cookie_name_fallback not in request.COOKIES
7643
):
7744
return response
7845

79-
# Most of the code below is taken directly from Django's SessionMiddleware.
80-
# Some changes (marked with NOTE:) were added to support the fallback cookie.
81-
82-
try:
83-
accessed = request.session.accessed
84-
modified = request.session.modified
85-
empty = request.session.is_empty()
86-
except AttributeError:
87-
pass
88-
else:
89-
# First check if we need to delete this cookie.
90-
# The session should be deleted only if the session is entirely empty
91-
# NOTE: This was changed to support both cookies
92-
if (
93-
settings.SESSION_COOKIE_NAME in request.COOKIES
94-
or self.cookie_name_fallback in request.COOKIES
95-
) and empty:
96-
for cookie_name in (
97-
settings.SESSION_COOKIE_NAME,
98-
self.cookie_name_fallback,
99-
):
100-
if cookie_name in request.COOKIES:
101-
response.delete_cookie(
102-
cookie_name,
103-
path=settings.SESSION_COOKIE_PATH,
104-
domain=settings.SESSION_COOKIE_DOMAIN,
105-
)
106-
else:
107-
if accessed:
108-
patch_vary_headers(response, ("Cookie",))
109-
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
110-
if request.session.get_expire_at_browser_close():
111-
max_age = None
112-
expires = None
113-
else:
114-
max_age = request.session.get_expiry_age()
115-
expires_time = time.time() + max_age
116-
expires = http_date(expires_time)
117-
# Save the session data and refresh the client cookie.
118-
# Skip session save for 500 responses, refs #3881.
119-
if response.status_code != 500:
120-
try:
121-
request.session.save()
122-
except UpdateError:
123-
raise SuspiciousOperation(
124-
"The request's session was deleted before the "
125-
"request completed. The user may have logged "
126-
"out in a concurrent request, for example."
127-
)
128-
129-
response.set_cookie(
130-
settings.SESSION_COOKIE_NAME,
131-
request.session.session_key,
132-
max_age=max_age,
133-
expires=expires,
134-
domain=settings.SESSION_COOKIE_DOMAIN,
135-
path=settings.SESSION_COOKIE_PATH,
136-
secure=settings.SESSION_COOKIE_SECURE or None,
137-
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
138-
samesite=settings.SESSION_COOKIE_SAMESITE,
139-
)
140-
141-
# NOTE: This was added to support the fallback cookie
142-
if settings.SESSION_COOKIE_SAMESITE == "None":
143-
# Forcibly set the session cookie to SameSite=None
144-
# This isn't supported in Django<3.1
145-
# https://github.com/django/django/pull/11894
146-
response.cookies[settings.SESSION_COOKIE_NAME][
147-
"samesite"
148-
] = "None"
149-
150-
# Set the fallback cookie in case the above cookie is rejected
151-
response.set_cookie(
152-
self.cookie_name_fallback,
153-
request.session.session_key,
154-
max_age=max_age,
155-
expires=expires,
156-
domain=settings.SESSION_COOKIE_DOMAIN,
157-
path=settings.SESSION_COOKIE_PATH,
158-
secure=settings.SESSION_COOKIE_SECURE or None,
159-
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
160-
# Use browser default in case SameSite=None is rejected.
161-
samesite=False,
162-
)
163-
return response
46+
return super().process_response(request, response)
16447

16548

16649
class ReferrerPolicyMiddleware:

readthedocs/rtd_tests/tests/test_middleware.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -250,17 +250,6 @@ def setUp(self):
250250

251251
self.user = create_user(username="owner", password="test")
252252

253-
@override_settings(SESSION_COOKIE_SAMESITE="None")
254-
def test_fallback_cookie(self):
255-
request = self.factory.get("/")
256-
response = HttpResponse()
257-
self.middleware.process_request(request)
258-
request.session["test"] = "value"
259-
response = self.middleware.process_response(request, response)
260-
261-
self.assertTrue(settings.SESSION_COOKIE_NAME in response.cookies)
262-
self.assertTrue(self.middleware.cookie_name_fallback in response.cookies)
263-
264253
@override_settings(SESSION_COOKIE_SAMESITE="None")
265254
def test_main_cookie_samesite_none(self):
266255
request = self.factory.get("/")
@@ -272,9 +261,6 @@ def test_main_cookie_samesite_none(self):
272261
self.assertEqual(
273262
response.cookies[settings.SESSION_COOKIE_NAME]["samesite"], "None"
274263
)
275-
self.assertEqual(
276-
response.cookies[self.middleware.cookie_name_fallback]["samesite"], ""
277-
)
278264

279265
def test_main_cookie_samesite_lax(self):
280266
request = self.factory.get("/")
@@ -286,7 +272,6 @@ def test_main_cookie_samesite_lax(self):
286272
self.assertEqual(
287273
response.cookies[settings.SESSION_COOKIE_NAME]["samesite"], "Lax"
288274
)
289-
self.assertTrue(self.test_main_cookie_samesite_none not in response.cookies)
290275

291276

292277
class TestNullCharactersMiddleware(TestCase):

0 commit comments

Comments
 (0)