From 5f35de056cb57d59e57534e727a21fce3b8e5b06 Mon Sep 17 00:00:00 2001 From: Fredrik Blomqvist Date: Fri, 20 Oct 2017 01:30:24 -0700 Subject: [PATCH] Add possibility to configure user claims location (refs #90) --- docs/options.rst | 2 ++ flask_jwt_extended/config.py | 4 ++++ flask_jwt_extended/jwt_manager.py | 1 + flask_jwt_extended/tokens.py | 7 ++++--- flask_jwt_extended/utils.py | 2 +- flask_jwt_extended/view_decorators.py | 4 ++-- tests/test_config.py | 3 +++ 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/options.rst b/docs/options.rst index 50c80c1b..f7e072b3 100644 --- a/docs/options.rst +++ b/docs/options.rst @@ -35,6 +35,8 @@ General Options: ``JWT_IDENTITY_CLAIM`` Claim in the tokens that is used as source of identity. For interoperativity, the JWT RFC recommends using ``'sub'``. Defaults to ``'identity'`` for legacy reasons. +``JWT_USER_CLAIMS`` Claim in the tokens that is used to store user claims. + Defaults to ``'user_claims'``. ================================= ========================================= diff --git a/flask_jwt_extended/config.py b/flask_jwt_extended/config.py index 579e4cc8..ed2165b8 100644 --- a/flask_jwt_extended/config.py +++ b/flask_jwt_extended/config.py @@ -227,6 +227,10 @@ def cookie_max_age(self): def identity_claim(self): return current_app.config['JWT_IDENTITY_CLAIM'] + @property + def user_claims(self): + return current_app.config['JWT_USER_CLAIMS'] + config = _Config() diff --git a/flask_jwt_extended/jwt_manager.py b/flask_jwt_extended/jwt_manager.py index f3e69caa..7e882cfd 100644 --- a/flask_jwt_extended/jwt_manager.py +++ b/flask_jwt_extended/jwt_manager.py @@ -185,6 +185,7 @@ def _set_default_configuration_options(app): app.config.setdefault('JWT_BLACKLIST_TOKEN_CHECKS', ['access', 'refresh']) app.config.setdefault('JWT_IDENTITY_CLAIM', 'identity') + app.config.setdefault('JWT_USER_CLAIMS', 'user_claims') def user_claims_loader(self, callback): """ diff --git a/flask_jwt_extended/tokens.py b/flask_jwt_extended/tokens.py index 031fce19..dc825adf 100644 --- a/flask_jwt_extended/tokens.py +++ b/flask_jwt_extended/tokens.py @@ -4,6 +4,7 @@ import jwt from flask_jwt_extended.exceptions import JWTDecodeError +from flask_jwt_extended.config import config def _create_csrf_token(): @@ -52,7 +53,7 @@ def encode_access_token(identity, secret, algorithm, expires_delta, fresh, # Add `user_claims` only is not empty or None. if user_claims: - token_data['user_claims'] = user_claims + token_data[config.user_claims] = user_claims if csrf: token_data['csrf'] = _create_csrf_token() @@ -107,8 +108,8 @@ def decode_jwt(encoded_token, secret, algorithm, csrf, identity_claim): if data['type'] == 'access': if 'fresh' not in data: raise JWTDecodeError("Missing claim: fresh") - if 'user_claims' not in data: - data['user_claims'] = {} + if config.user_claims not in data: + data[config.user_claims] = {} if csrf: if 'csrf' not in data: raise JWTDecodeError("Missing claim: csrf") diff --git a/flask_jwt_extended/utils.py b/flask_jwt_extended/utils.py index 7dfa1dc0..cecfb08b 100644 --- a/flask_jwt_extended/utils.py +++ b/flask_jwt_extended/utils.py @@ -37,7 +37,7 @@ def get_jwt_claims(): in the JWT that is accessing the endpoint. If no custom user claims are present, an empty dict is returned instead. """ - return get_raw_jwt().get('user_claims', {}) + return get_raw_jwt().get(config.user_claims, {}) def get_current_user(): diff --git a/flask_jwt_extended/view_decorators.py b/flask_jwt_extended/view_decorators.py index e08ed8de..4f68ba15 100644 --- a/flask_jwt_extended/view_decorators.py +++ b/flask_jwt_extended/view_decorators.py @@ -216,8 +216,8 @@ def _decode_jwt_from_request(request_type): # Check if the custom claims in access tokens are valid if request_type == 'access': - if not verify_token_claims(decoded_token['user_claims']): - raise UserClaimsVerificationError('user_claims verification failed') + if not verify_token_claims(decoded_token[config.user_claims]): + raise UserClaimsVerificationError('User claims verification failed') # If blacklisting is enabled, see if this token has been revoked if _token_blacklisted(decoded_token, request_type): diff --git a/tests/test_config.py b/tests/test_config.py index f67aad03..988bad79 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -55,6 +55,7 @@ def test_default_configs(self): self.assertEqual(config.cookie_max_age, None) self.assertEqual(config.identity_claim, 'identity') + self.assertEqual(config.user_claims, 'user_claims') def test_override_configs(self): self.app.config['JWT_TOKEN_LOCATION'] = ['cookies'] @@ -89,6 +90,7 @@ def test_override_configs(self): self.app.secret_key = 'banana' self.app.config['JWT_IDENTITY_CLAIM'] = 'foo' + self.app.config['JWT_USER_CLAIMS'] = 'bar' with self.app.test_request_context(): self.assertEqual(config.token_location, ['cookies']) @@ -127,6 +129,7 @@ def test_override_configs(self): self.assertEqual(config.cookie_max_age, 2147483647) self.assertEqual(config.identity_claim, 'foo') + self.assertEqual(config.user_claims, 'bar') def test_invalid_config_options(self): with self.app.test_request_context():