Skip to content

Commit ddbaf00

Browse files
committed
Refactored PyJWT._validate_claims into multiple PyJWT._validate_*
submethods to eliminate a lot of complexity.
1 parent a3f2ec3 commit ddbaf00

File tree

1 file changed

+69
-41
lines changed

1 file changed

+69
-41
lines changed

jwt/api_jwt.py

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ def _get_default_options():
2626
'verify_nbf': True,
2727
'verify_iat': True,
2828
'verify_aud': True,
29+
'verify_iss': True
2930
}
3031

31-
def encode(self, payload, key, algorithm='HS256', headers=None, json_encoder=None):
32+
def encode(self, payload, key, algorithm='HS256', headers=None,
33+
json_encoder=None):
3234
# Check that we get a mapping
3335
if not isinstance(payload, Mapping):
34-
raise TypeError('Expecting a mapping object, as json web token only'
35-
'support json objects.')
36+
raise TypeError('Expecting a mapping object, as JWT only supports '
37+
'JSON objects as payloads.')
3638

3739
# Payload
3840
for time_claim in ['exp', 'iat', 'nbf']:
@@ -81,52 +83,78 @@ def _validate_claims(self, payload, audience=None, issuer=None, leeway=0,
8183
now = timegm(datetime.utcnow().utctimetuple())
8284

8385
if 'iat' in payload and options.get('verify_iat'):
84-
try:
85-
iat = int(payload['iat'])
86-
except ValueError:
87-
raise DecodeError('Issued At claim (iat) must be an integer.')
88-
89-
if iat > (now + leeway):
90-
raise InvalidIssuedAtError('Issued At claim (iat) cannot be in'
91-
' the future.')
86+
self._validate_iat(payload, now, leeway)
9287

9388
if 'nbf' in payload and options.get('verify_nbf'):
94-
try:
95-
nbf = int(payload['nbf'])
96-
except ValueError:
97-
raise DecodeError('Not Before claim (nbf) must be an integer.')
98-
99-
if nbf > (now + leeway):
100-
raise ImmatureSignatureError('The token is not yet valid (nbf)')
89+
self._validate_nbf(payload, now, leeway)
10190

10291
if 'exp' in payload and options.get('verify_exp'):
103-
try:
104-
exp = int(payload['exp'])
105-
except ValueError:
106-
raise DecodeError('Expiration Time claim (exp) must be an'
107-
' integer.')
108-
109-
if exp < (now - leeway):
110-
raise ExpiredSignatureError('Signature has expired')
111-
112-
if 'aud' in payload and options.get('verify_aud'):
113-
audience_claims = payload['aud']
114-
if isinstance(audience_claims, string_types):
115-
audience_claims = [audience_claims]
116-
if not isinstance(audience_claims, list):
117-
raise InvalidAudienceError('Invalid claim format in token')
118-
if any(not isinstance(c, string_types) for c in audience_claims):
119-
raise InvalidAudienceError('Invalid claim format in token')
120-
if audience not in audience_claims:
121-
raise InvalidAudienceError('Invalid audience')
122-
elif audience is not None:
92+
self._validate_exp(payload, now, leeway)
93+
94+
if options.get('verify_iss'):
95+
self._validate_iss(payload, issuer)
96+
97+
if options.get('verify_aud'):
98+
self._validate_aud(payload, audience)
99+
100+
def _validate_iat(self, payload, now, leeway):
101+
try:
102+
iat = int(payload['iat'])
103+
except ValueError:
104+
raise DecodeError('Issued At claim (iat) must be an integer.')
105+
106+
if iat > (now + leeway):
107+
raise InvalidIssuedAtError('Issued At claim (iat) cannot be in'
108+
' the future.')
109+
110+
def _validate_nbf(self, payload, now, leeway):
111+
try:
112+
nbf = int(payload['nbf'])
113+
except ValueError:
114+
raise DecodeError('Not Before claim (nbf) must be an integer.')
115+
116+
if nbf > (now + leeway):
117+
raise ImmatureSignatureError('The token is not yet valid (nbf)')
118+
119+
def _validate_exp(self, payload, now, leeway):
120+
try:
121+
exp = int(payload['exp'])
122+
except ValueError:
123+
raise DecodeError('Expiration Time claim (exp) must be an'
124+
' integer.')
125+
126+
if exp < (now - leeway):
127+
raise ExpiredSignatureError('Signature has expired')
128+
129+
def _validate_aud(self, payload, audience):
130+
if audience is None and 'aud' not in payload:
131+
return
132+
133+
if audience is not None and 'aud' not in payload:
123134
# Application specified an audience, but it could not be
124135
# verified since the token does not contain a claim.
125136
raise InvalidAudienceError('No audience claim in token')
126137

127-
if issuer is not None:
128-
if payload.get('iss') != issuer:
129-
raise InvalidIssuerError('Invalid issuer')
138+
audience_claims = payload['aud']
139+
140+
if isinstance(audience_claims, string_types):
141+
audience_claims = [audience_claims]
142+
if not isinstance(audience_claims, list):
143+
raise InvalidAudienceError('Invalid claim format in token')
144+
if any(not isinstance(c, string_types) for c in audience_claims):
145+
raise InvalidAudienceError('Invalid claim format in token')
146+
if audience not in audience_claims:
147+
raise InvalidAudienceError('Invalid audience')
148+
149+
def _validate_iss(self, payload, issuer):
150+
if issuer is None:
151+
return
152+
153+
if 'iss' not in payload:
154+
raise InvalidIssuerError('Token does not contain an iss claim')
155+
156+
if payload['iss'] != issuer:
157+
raise InvalidIssuerError('Invalid issuer')
130158

131159

132160
_jwt_global_obj = PyJWT()

0 commit comments

Comments
 (0)