Skip to content

Commit d1fbc15

Browse files
author
Michael Davis
committed
Validate sub and jti claims
1 parent 9e45516 commit d1fbc15

File tree

3 files changed

+105
-2
lines changed

3 files changed

+105
-2
lines changed

jose/jws.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ def _load(jwt):
157157

158158
def _verify_signature(payload, signing_input, header, signature, key='', algorithms=None):
159159

160-
alg = header['alg']
160+
alg = header.get('alg')
161+
if not alg:
162+
raise JWSError('No algorithm was specified in the JWS header.')
161163

162164
if algorithms is not None and alg not in algorithms:
163165
raise JWSError('The specified alg value is not allowed')

jose/jwt.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,15 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
6666
the provided claim.
6767
options (dict): A dictionary of options for skipping validation steps.
6868
69-
default = {
69+
defaults = {
7070
'verify_signature': True,
7171
'verify_aud': True,
7272
'verify_iat': True,
7373
'verify_exp': True,
7474
'verify_nbf': True,
75+
'verify_iss': True,
76+
'verify_sub': True,
77+
'verify_jti': True,
7578
'leeway': 0,
7679
}
7780
@@ -96,6 +99,8 @@ def decode(token, key, algorithms=None, options=None, audience=None, issuer=None
9699
'verify_exp': True,
97100
'verify_nbf': True,
98101
'verify_iss': True,
102+
'verify_sub': True,
103+
'verify_jti': True,
99104
'leeway': 0,
100105
}
101106

@@ -245,6 +250,50 @@ def _validate_iss(claims, issuer=None):
245250
raise JWTClaimsError('Invalid issuer')
246251

247252

253+
def _validate_sub(claims):
254+
"""Validates that the 'sub' claim is valid.
255+
256+
The "sub" (subject) claim identifies the principal that is the
257+
subject of the JWT. The claims in a JWT are normally statements
258+
about the subject. The subject value MUST either be scoped to be
259+
locally unique in the context of the issuer or be globally unique.
260+
The processing of this claim is generally application specific. The
261+
"sub" value is a case-sensitive string containing a StringOrURI
262+
value. Use of this claim is OPTIONAL.
263+
264+
Args:
265+
claims (dict): The claims dictionary to validate.
266+
"""
267+
268+
if 'sub' not in claims:
269+
return
270+
271+
if not isinstance(claims['sub'], string_types):
272+
raise JWTClaimsError('Subject must be a string.')
273+
274+
275+
def _validate_jti(claims):
276+
"""Validates that the 'jti' claim is valid.
277+
278+
The "jti" (JWT ID) claim provides a unique identifier for the JWT.
279+
The identifier value MUST be assigned in a manner that ensures that
280+
there is a negligible probability that the same value will be
281+
accidentally assigned to a different data object; if the application
282+
uses multiple issuers, collisions MUST be prevented among values
283+
produced by different issuers as well. The "jti" claim can be used
284+
to prevent the JWT from being replayed. The "jti" value is a case-
285+
sensitive string. Use of this claim is OPTIONAL.
286+
287+
Args:
288+
claims (dict): The claims dictionary to validate.
289+
"""
290+
if 'jti' not in claims:
291+
return
292+
293+
if not isinstance(claims['jti'], string_types):
294+
raise JWTClaimsError('JWT ID must be a string.')
295+
296+
248297
def _validate_claims(claims, audience=None, issuer=None, options=None):
249298

250299
leeway = options.get('leeway', 0)
@@ -269,3 +318,9 @@ def _validate_claims(claims, audience=None, issuer=None, options=None):
269318

270319
if options.get('verify_iss'):
271320
_validate_iss(claims, issuer=issuer)
321+
322+
if options.get('verify_sub'):
323+
_validate_sub(claims)
324+
325+
if options.get('verify_jti'):
326+
_validate_jti(claims)

tests/test_jwt.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,49 @@ def test_iss_invalid(self, key):
330330
token = jwt.encode(claims, key)
331331
with pytest.raises(JWTError):
332332
jwt.decode(token, key, issuer='another')
333+
334+
def test_sub_string(self, key):
335+
336+
sub = 'subject'
337+
338+
claims = {
339+
'sub': sub
340+
}
341+
342+
token = jwt.encode(claims, key)
343+
jwt.decode(token, key)
344+
345+
def test_sub_invalid(self, key):
346+
347+
sub = 1
348+
349+
claims = {
350+
'sub': sub
351+
}
352+
353+
token = jwt.encode(claims, key)
354+
with pytest.raises(JWTError):
355+
jwt.decode(token, key)
356+
357+
def test_jti_string(self, key):
358+
359+
jti = 'JWT ID'
360+
361+
claims = {
362+
'jti': jti
363+
}
364+
365+
token = jwt.encode(claims, key)
366+
jwt.decode(token, key)
367+
368+
def test_jti_invalid(self, key):
369+
370+
jti = 1
371+
372+
claims = {
373+
'jti': jti
374+
}
375+
376+
token = jwt.encode(claims, key)
377+
with pytest.raises(JWTError):
378+
jwt.decode(token, key)

0 commit comments

Comments
 (0)