in src/olympia/api/jwt_auth.py [0:0]
def jwt_decode_handler(token, get_api_key=APIKey.get_jwt_key):
"""Decodes a JWT token."""
# If you raise AuthenticationFailed from this method, its value will
# be displayed to the client. Be careful not to reveal anything
# sensitive. When you raise other exceptions, the user will see
# a generic failure message.
token_data = jwt.decode(
token,
options={
'verify_signature': False,
'verify_exp': False,
'verify_nbf': False,
'verify_iat': False,
'verify_aud': False,
},
algorithms=[settings.JWT_AUTH['JWT_ALGORITHM']],
)
if 'iss' not in token_data:
log.info(f'No issuer in JWT auth token: {token_data}')
raise exceptions.AuthenticationFailed(
detail='JWT iss (issuer) claim is missing.'
)
try:
api_key = get_api_key(key=token_data['iss'])
except ObjectDoesNotExist as exc:
log.info('No API key for JWT issuer: {}'.format(token_data['iss']))
raise exceptions.AuthenticationFailed(
detail='Unknown JWT iss (issuer).'
) from exc
# TODO: add nonce checking to prevent replays. bug 1213354.
options = {
'verify_signature': True,
'verify_exp': True,
'verify_nbf': False,
'verify_iat': True,
'verify_aud': False,
'require': ['exp', 'iat'],
}
try:
now = timegm(datetime.utcnow().utctimetuple())
payload = jwt.decode(
token,
api_key.secret,
options=options,
leeway=settings.JWT_AUTH['JWT_LEEWAY'],
algorithms=[settings.JWT_AUTH['JWT_ALGORITHM']],
)
# Verify clock skew for future iat-values pyjwt removed that check in
# https://github.com/jpadilla/pyjwt/pull/252/
# `verify_iat` is still in options because pyjwt still validates
# that `iat` is a proper number.
if int(payload['iat']) > (now + settings.JWT_AUTH['JWT_LEEWAY']):
raise jwt.InvalidIssuedAtError(
'Issued At claim (iat) cannot be in the future.'
)
except jwt.MissingRequiredClaimError as exc:
log.info(
'Missing required claim during JWT authentication: '
'{e.__class__.__name__}: {e}'.format(e=exc)
)
raise exceptions.AuthenticationFailed(detail=f'Invalid JWT: {exc}.') from exc
except (jwt.exceptions.ImmatureSignatureError, jwt.InvalidIssuedAtError) as exc:
log.info(
'Invalid iat during JWT authentication: {e.__class__.__name__}: {e}'.format(
e=exc
)
)
raise exceptions.AuthenticationFailed(
detail='JWT iat (issued at time) is invalid. Make sure your '
'system clock is synchronized with something like TLSdate.'
) from exc
except Exception as exc:
log.warning(
'Unhandled exception during JWT authentication: '
'{e.__class__.__name__}: {e}'.format(e=exc)
)
raise
if payload['exp'] - payload['iat'] > settings.MAX_APIKEY_JWT_AUTH_TOKEN_LIFETIME:
log.info(
'JWT auth: expiration is too long; iss={iss}, iat={iat}, exp={exp}'.format(
**payload
)
)
raise exceptions.AuthenticationFailed(
detail='JWT exp (expiration) is too long.'
)
return payload