Skip to content

Commit

Permalink
Require tweak (#280)
Browse files Browse the repository at this point in the history
* Use require options as a list, instead of booleans

Deprecate the use of the boolean options

* Add test for the new require option

* Add documentation on how to use the require option

Co-authored-by: Pau Ruiz i Safont <psafont@ebi.ac.uk>
  • Loading branch information
psafont and psafont committed May 14, 2020
1 parent c2c91a7 commit 008490a
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 12 deletions.
10 changes: 10 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,13 @@ Issued At Claim (iat)
jwt.encode({'iat': 1371720939}, 'secret')
jwt.encode({'iat': datetime.utcnow()}, 'secret')
Requiring Presence of Claims
----------------------------

If you wish to require one or more claims to be present in the claimset, you can set the ``require`` paramenter to include these claims.

.. code-block:: python
>>jwt.decode(encoded, options={'require': ['exp', 'iss', 'sub']})
{u'exp': 1371720939, u'iss': u'urn:foo', u'sub': u'25c37522-f148-4cbf-8ee6-c4a9718dd0af'}
34 changes: 22 additions & 12 deletions jwt/api_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,19 @@

class PyJWT(PyJWS):
header_type = "JWT"
deprecated_requires = ["require_exp", "require_iat", "require_nbf"]

@staticmethod
def _get_default_options():
# type: () -> Dict[str, bool]
# type: () -> Dict[str, Union[bool, List[str]]]
return {
"verify_signature": True,
"verify_exp": True,
"verify_nbf": True,
"verify_iat": True,
"verify_aud": True,
"verify_iss": True,
"require_exp": False,
"require_iat": False,
"require_nbf": False,
"require": [],
}

def encode(
Expand Down Expand Up @@ -128,6 +127,22 @@ def _validate_claims(
DeprecationWarning,
)

verify_claims = {
required
for required in self.deprecated_requires
if required in options
}
require_options = options.setdefault("require", [])
for opt in verify_claims:
opt_claim = opt.split("require_", 1)[1]
if options[opt]:
require_options.append(opt_claim)
warnings.warn(
"The {0} parameter is deprecated. Please add {1} to"
" the require list in options instead".format(opt, opt_claim),
DeprecationWarning,
)

if isinstance(leeway, timedelta):
leeway = leeway.total_seconds()

Expand All @@ -154,14 +169,9 @@ def _validate_claims(
self._validate_aud(payload, audience)

def _validate_required_claims(self, payload, options):
if options.get("require_exp") and payload.get("exp") is None:
raise MissingRequiredClaimError("exp")

if options.get("require_iat") and payload.get("iat") is None:
raise MissingRequiredClaimError("iat")

if options.get("require_nbf") and payload.get("nbf") is None:
raise MissingRequiredClaimError("nbf")
for claim in options.get("require", []):
if payload.get(claim) is None:
raise MissingRequiredClaimError(claim)

def _validate_iat(self, payload, now, leeway):
try:
Expand Down
15 changes: 15 additions & 0 deletions tests/test_api_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,21 @@ def test_decode_should_raise_error_if_nbf_required_but_not_present(

assert exc.value.claim == "nbf"

def test_decode_should_raise_error_if_claim_required_but_not_present(
self, jwt
):
claim = "sub"
payload = {
"some": "payload",
# claim not present
}
token = jwt.encode(payload, "secret")

with pytest.raises(MissingRequiredClaimError) as exc:
jwt.decode(token, "secret", options={"require": [claim]})

assert exc.value.claim == claim

def test_skip_check_signature(self, jwt):
token = (
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
Expand Down

0 comments on commit 008490a

Please sign in to comment.