0001-auth_oidc-enforce-SameSite-Lax-on-the-state-cookie-4.patch
src/authentic2/compat/cookies.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import django |
|
18 | ||
19 |
if django.VERSION < (2, 1): |
|
20 |
# Copied from Django >=2.1 / django.http.cookies |
|
21 |
from http import cookies |
|
22 |
cookies.Morsel._reserved.setdefault('samesite', 'SameSite') |
src/authentic2_auth_oidc/views.py | ||
---|---|---|
21 | 21 | |
22 | 22 |
import requests |
23 | 23 | |
24 |
import django |
|
24 | 25 |
from django.conf import settings |
25 | 26 |
from django.core import signing |
26 | 27 |
from django.urls import reverse |
... | ... | |
31 | 32 |
from django.views.generic.base import View |
32 | 33 |
from django.http import HttpResponseBadRequest |
33 | 34 | |
35 |
import authentic2.compat.cookies # F401 |
|
34 | 36 |
from authentic2.decorators import setting_enabled |
35 | 37 |
from authentic2.utils import redirect, login, good_next_url, authenticate |
36 | 38 | |
... | ... | |
90 | 92 |
logger.debug('auth_oidc: sent request %s to authorization endpoint "%s"', |
91 | 93 |
params, provider.authorization_endpoint) |
92 | 94 |
response = redirect(request, provider.authorization_endpoint, params=params, resolve=False) |
93 |
response.set_cookie( |
|
94 |
'oidc-state', value=state_id, path=reverse('oidc-login-callback'), |
|
95 |
httponly=True, secure=request.is_secure()) |
|
95 | ||
96 |
# As the oidc-state is used during a redirect from a third-party, we need |
|
97 |
# it to user SameSite=Lax. See |
|
98 |
# https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Set-Cookie/SameSite |
|
99 |
# for more explanations. |
|
100 |
if django.VERSION < (2, 1): |
|
101 |
response.set_cookie( |
|
102 |
'oidc-state', value=state_id, path=reverse('oidc-login-callback'), |
|
103 |
httponly=True, secure=request.is_secure()) |
|
104 |
# work around lack of samesite parameter to set_cookie() in Django 1.11 |
|
105 |
# it also needs monkeypatch from authentic2.compat.cookies. |
|
106 |
response.cookies['oidc-state']['samesite'] = 'Lax' |
|
107 |
else: |
|
108 |
response.set_cookie( |
|
109 |
'oidc-state', value=state_id, path=reverse('oidc-login-callback'), |
|
110 |
httponly=True, secure=request.is_secure(), samesite='lax') |
|
96 | 111 |
return response |
97 | 112 | |
98 | 113 |
tests/test_auth_oidc.py | ||
---|---|---|
818 | 818 |
response = app.get('/login/?next=/whatever/') |
819 | 819 |
assert oidc_provider.name in response.text |
820 | 820 |
response = response.click(oidc_provider.name) |
821 |
# As the oidc-state is used during a redirect from a third-party, we need |
|
822 |
# it to be lax. |
|
823 |
assert re.search('Set-Cookie.* oidc-state=.*SameSite=lax', str(response)) |
|
821 | 824 |
qs = urlparse.parse_qs(urlparse.urlparse(response.location).query) |
822 | 825 |
state = qs['state'] |
823 | 826 | |
824 |
- |