Skip to content

Commit 183c3c3

Browse files
committed
Use one header for all csrf token double sumbits
1 parent 49cdf25 commit 183c3c3

File tree

4 files changed

+10
-32
lines changed

4 files changed

+10
-32
lines changed

flask_jwt_extended/config.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import datetime
22
from flask import current_app
33

4-
# TODO move this to the docs
5-
# blacklist storage options (simplekv). If using a storage option that supports
6-
# the simplekv.TimeToLiveMixin (example: redis, memcached), the TTL will be
7-
# automatically set to 15 minutes after the token expires (to account for
8-
# clock drift between different jwt providers/consumers).
9-
#
10-
# See: http://pythonhosted.org/simplekv/index.html#simplekv.TimeToLiveMixin
11-
124

135
# TODO support for cookies and headers at the same time. This could be useful
146
# for using cookies in a web browser (more secure), and headers in a mobile
@@ -29,15 +21,11 @@
2921
ACCESS_COOKIE_PATH = None
3022
REFRESH_COOKIE_PATH = None
3123

32-
# TODO set domain for the cookie
33-
# TODO only one header for both access and refresh tokens, as we will only be
34-
# checking one of those at a time
3524
# Options for using double submit for verifying CSRF tokens
3625
COOKIE_CSRF_PROTECT = True
3726
ACCESS_CSRF_COOKIE_NAME = 'csrf_access_token'
3827
REFRESH_CSRF_COOKIE_NAME = 'csrf_refresh_token'
39-
ACCESS_CSRF_HEADER_NAME = 'X-CSRF-ACCESS-TOKEN'
40-
REFRESH_CSRF_HEADER_NAME = 'X-CSRF-REFRESH-TOKEN'
28+
CSRF_HEADER_NAME = 'X-CSRF-TOKEN'
4129

4230
# How long an a token will live before they expire.
4331
ACCESS_TOKEN_EXPIRES = datetime.timedelta(minutes=15)
@@ -100,12 +88,8 @@ def get_refresh_csrf_cookie_name():
10088
return current_app.config.get('JWT_REFRESH_CSRF_COOKIE_NAME', REFRESH_CSRF_COOKIE_NAME)
10189

10290

103-
def get_access_csrf_header_name():
104-
return current_app.config.get('JWT_ACCESS_CSRF_HEADER_NAME', ACCESS_CSRF_HEADER_NAME)
105-
106-
107-
def get_refresh_csrf_header_name():
108-
return current_app.config.get('JWT_REFRESH_CSRF_HEADER_NAME', REFRESH_CSRF_HEADER_NAME)
91+
def get_csrf_header_name():
92+
return current_app.config.get('JWT_CSRF_HEADER_NAME', CSRF_HEADER_NAME)
10993

11094

11195
def get_jwt_header_type():

flask_jwt_extended/utils.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
get_cookie_csrf_protect, get_access_csrf_cookie_name, \
1919
get_refresh_cookie_name, get_refresh_cookie_path, \
2020
get_refresh_csrf_cookie_name, get_token_location, \
21-
get_access_csrf_header_name, get_refresh_csrf_header_name
21+
get_csrf_header_name
2222
from flask_jwt_extended.exceptions import JWTEncodeError, JWTDecodeError, \
2323
InvalidHeaderError, NoAuthorizationError, WrongTokenError, RevokedTokenError, \
2424
FreshTokenRequired
@@ -179,10 +179,8 @@ def _decode_jwt_from_headers():
179179
def _decode_jwt_from_cookies(type):
180180
if type == 'access':
181181
cookie_key = get_access_cookie_name()
182-
csrf_header_key = get_access_csrf_header_name()
183182
else:
184183
cookie_key = get_refresh_cookie_name()
185-
csrf_header_key = get_refresh_csrf_header_name()
186184

187185
token = request.cookies.get(cookie_key)
188186
if not token:
@@ -192,6 +190,7 @@ def _decode_jwt_from_cookies(type):
192190
token = _decode_jwt(token, secret, algorithm)
193191

194192
if get_cookie_csrf_protect():
193+
csrf_header_key = get_csrf_header_name()
195194
csrf = request.headers.get(csrf_header_key, None)
196195
if not csrf or not safe_str_cmp(csrf, token['csrf']):
197196
raise NoAuthorizationError("Missing or invalid csrf double submit header")

tests/test_config.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
get_token_location, get_cookie_secure, get_access_cookie_name, \
1111
get_refresh_cookie_name, get_access_cookie_path, get_refresh_cookie_path, \
1212
get_cookie_csrf_protect, get_access_csrf_cookie_name, \
13-
get_refresh_csrf_cookie_name, get_access_csrf_header_name, \
14-
get_refresh_csrf_header_name
13+
get_refresh_csrf_cookie_name, get_csrf_header_name
1514
from flask_jwt_extended import JWTManager
1615

1716

@@ -24,7 +23,6 @@ def setUp(self):
2423
JWTManager(self.app)
2524
self.client = self.app.test_client()
2625

27-
#57, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105
2826
def test_default_configs(self):
2927
with self.app.test_request_context():
3028
self.assertEqual(get_token_location(), 'headers')
@@ -39,8 +37,7 @@ def test_default_configs(self):
3937
self.assertEqual(get_cookie_csrf_protect(), True)
4038
self.assertEqual(get_access_csrf_cookie_name(), 'csrf_access_token')
4139
self.assertEqual(get_refresh_csrf_cookie_name(), 'csrf_refresh_token')
42-
self.assertEqual(get_access_csrf_header_name(), 'X-CSRF-ACCESS-TOKEN')
43-
self.assertEqual(get_refresh_csrf_header_name(), 'X-CSRF-REFRESH-TOKEN')
40+
self.assertEqual(get_csrf_header_name(), 'X-CSRF-TOKEN')
4441

4542
self.assertEqual(get_access_expires(), timedelta(minutes=15))
4643
self.assertEqual(get_refresh_expires(), timedelta(days=30))
@@ -62,8 +59,7 @@ def test_override_configs(self):
6259
self.app.config['JWT_COOKIE_CSRF_PROTECT'] = False
6360
self.app.config['JWT_ACCESS_CSRF_COOKIE_NAME'] = 'banana1a'
6461
self.app.config['JWT_REFRESH_CSRF_COOKIE_NAME'] = 'banana2a'
65-
self.app.config['JWT_ACCESS_CSRF_HEADER_NAME'] = 'banana1b'
66-
self.app.config['JWT_REFRESH_CSRF_HEADER_NAME'] = 'banana2b'
62+
self.app.config['JWT_CSRF_HEADER_NAME'] = 'bananaaaa'
6763

6864
self.app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=5)
6965
self.app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=7)
@@ -85,8 +81,7 @@ def test_override_configs(self):
8581
self.assertEqual(get_cookie_csrf_protect(), False)
8682
self.assertEqual(get_access_csrf_cookie_name(), 'banana1a')
8783
self.assertEqual(get_refresh_csrf_cookie_name(), 'banana2a')
88-
self.assertEqual(get_access_csrf_header_name(), 'banana1b')
89-
self.assertEqual(get_refresh_csrf_header_name(), 'banana2b')
84+
self.assertEqual(get_csrf_header_name(), 'bananaaaa')
9085

9186
self.assertEqual(get_access_expires(), timedelta(minutes=5))
9287
self.assertEqual(get_refresh_expires(), timedelta(days=7))

tests/test_protected_endpoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ def test_access_endpoints_with_cookies_and_csrf(self):
502502

503503
# Try with logged in and good double submit token
504504
response = self.client.get('/api/protected',
505-
headers={'X-CSRF-ACCESS-TOKEN': access_csrf})
505+
headers={'X-CSRF-TOKEN': access_csrf})
506506
status_code = response.status_code
507507
data = json.loads(response.get_data(as_text=True))
508508
self.assertEqual(status_code, 200)

0 commit comments

Comments
 (0)