Skip to content

Commit

Permalink
Merge pull request #149 from matthewstory/master
Browse files Browse the repository at this point in the history
[#148] add unset_access_cookies and unset_refresh_cookies functions
  • Loading branch information
vimalloc authored May 5, 2018
2 parents 2eb6998 + 31a4518 commit 9250430
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 19 deletions.
5 changes: 3 additions & 2 deletions flask_jwt_extended/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
from .utils import (
create_refresh_token, create_access_token, get_jwt_identity,
get_jwt_claims, set_access_cookies, set_refresh_cookies,
unset_jwt_cookies, get_raw_jwt, get_current_user, current_user,
get_jti, decode_token, get_csrf_token
unset_jwt_cookies, unset_access_cookies, unset_refresh_cookies,
get_raw_jwt, get_current_user, current_user, get_jti, decode_token,
get_csrf_token
)

__version__ = '3.8.1'
54 changes: 42 additions & 12 deletions flask_jwt_extended/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,18 +281,23 @@ def unset_jwt_cookies(response):
:param response: The Flask response object to delete the JWT cookies in.
"""
unset_access_cookies(response)
unset_refresh_cookies(response)


def unset_access_cookies(response):
"""
takes a flask response object, and configures it to unset (delete) the
access token from the response cookies. if `jwt_csrf_in_cookies`
(see :ref:`configuration options`) is `true`, this will also remove the
access csrf double submit value from the response cookies as well.
:param response: the flask response object to delete the jwt cookies in.
"""
if not config.jwt_in_cookies:
raise RuntimeWarning("unset_refresh_cookies() called without "
"'JWT_TOKEN_LOCATION' configured to use cookies")

response.set_cookie(config.refresh_cookie_name,
value='',
expires=0,
secure=config.cookie_secure,
httponly=True,
domain=config.cookie_domain,
path=config.refresh_cookie_path,
samesite=config.cookie_samesite)
response.set_cookie(config.access_cookie_name,
value='',
expires=0,
Expand All @@ -303,19 +308,44 @@ def unset_jwt_cookies(response):
samesite=config.cookie_samesite)

if config.csrf_protect and config.csrf_in_cookies:
response.set_cookie(config.refresh_csrf_cookie_name,
response.set_cookie(config.access_csrf_cookie_name,
value='',
expires=0,
secure=config.cookie_secure,
httponly=False,
domain=config.cookie_domain,
path=config.refresh_csrf_cookie_path,
path=config.access_csrf_cookie_path,
samesite=config.cookie_samesite)
response.set_cookie(config.access_csrf_cookie_name,


def unset_refresh_cookies(response):
"""
takes a flask response object, and configures it to unset (delete) the
refresh token from the response cookies. if `jwt_csrf_in_cookies`
(see :ref:`configuration options`) is `true`, this will also remove the
refresh csrf double submit value from the response cookies as well.
:param response: the flask response object to delete the jwt cookies in.
"""
if not config.jwt_in_cookies:
raise RuntimeWarning("unset_refresh_cookies() called without "
"'JWT_TOKEN_LOCATION' configured to use cookies")

response.set_cookie(config.refresh_cookie_name,
value='',
expires=0,
secure=config.cookie_secure,
httponly=True,
domain=config.cookie_domain,
path=config.refresh_cookie_path,
samesite=config.cookie_samesite)

if config.csrf_protect and config.csrf_in_cookies:
response.set_cookie(config.refresh_csrf_cookie_name,
value='',
expires=0,
secure=config.cookie_secure,
httponly=False,
domain=config.cookie_domain,
path=config.access_csrf_cookie_path,
path=config.refresh_csrf_cookie_path,
samesite=config.cookie_samesite)
36 changes: 31 additions & 5 deletions tests/test_cookies.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from flask_jwt_extended import (
jwt_required, JWTManager, jwt_refresh_token_required, create_access_token,
create_refresh_token, set_access_cookies, set_refresh_cookies,
unset_jwt_cookies, jwt_optional
unset_jwt_cookies, unset_access_cookies, unset_refresh_cookies, jwt_optional
)

def _get_cookie_from_response(response, cookie_name):
Expand Down Expand Up @@ -46,6 +46,18 @@ def delete_tokens():
unset_jwt_cookies(resp)
return resp

@app.route('/delete_access_tokens', methods=['GET'])
def delete_access_tokens():
resp = jsonify(access_revoked=True)
unset_access_cookies(resp)
return resp

@app.route('/delete_refresh_tokens', methods=['GET'])
def delete_refresh_tokens():
resp = jsonify(refresh_revoked=True)
unset_refresh_cookies(resp)
return resp

@app.route('/protected', methods=['GET'])
@jwt_required
def protected():
Expand Down Expand Up @@ -75,12 +87,12 @@ def optional_post_protected():


@pytest.mark.parametrize("options", [
('/refresh_token', 'refresh_token_cookie', '/refresh_protected'),
('/access_token', 'access_token_cookie', '/protected')
('/refresh_token', 'refresh_token_cookie', '/refresh_protected', '/delete_refresh_tokens'),
('/access_token', 'access_token_cookie', '/protected', '/delete_access_tokens')
])
def test_jwt_refresh_required_with_cookies(app, options):
test_client = app.test_client()
auth_url, cookie_name, protected_url = options
auth_url, cookie_name, protected_url, delete_url = options

# Test without cookies
response = test_client.get(protected_url)
Expand All @@ -94,7 +106,17 @@ def test_jwt_refresh_required_with_cookies(app, options):
assert response.get_json() == {'foo': 'bar'}

# Test after issuing a 'logout' to delete the cookies
test_client.get('/delete_tokens')
test_client.get(delete_url)
response = test_client.get(protected_url)
assert response.status_code == 401
assert response.get_json() == {'msg': 'Missing cookie "{}"'.format(cookie_name)}

# log back in once more to test that clearing all tokens works
test_client.get(auth_url)
response = test_client.get(protected_url)
assert response.status_code == 200

test_client.get("/delete_tokens")
response = test_client.get(protected_url)
assert response.status_code == 401
assert response.get_json() == {'msg': 'Missing cookie "{}"'.format(cookie_name)}
Expand Down Expand Up @@ -217,6 +239,10 @@ def test_setting_cookies_wihout_cookies_enabled(app):
assert response.status_code == 500
response = test_client.get('/delete_tokens')
assert response.status_code == 500
response = test_client.get('/delete_access_tokens')
assert response.status_code == 500
response = test_client.get('/delete_refresh_tokens')
assert response.status_code == 500


def test_default_cookie_options(app):
Expand Down

0 comments on commit 9250430

Please sign in to comment.