Last active
March 21, 2024 18:27
-
-
Save spencerkittleson/f717d90c8035c701e6127e1b3ce10c57 to your computer and use it in GitHub Desktop.
Validate a JWT token is from a configured issuer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import requests | |
import json | |
import jwt | |
from dotenv import dotenv_values | |
def get_json(endpoint: str): | |
response = requests.get(endpoint) | |
response.raise_for_status() | |
return json.loads(response.content) | |
def verify_jwt(token, issuer, discovery=None): | |
"""Only public key verification is needed here""" | |
try: | |
# Decode the JWT without verifying the signature | |
unvalidated_decoded_token = jwt.decode( | |
token, options={"verify_signature": False}) | |
# Check if the issuer matches | |
if unvalidated_decoded_token['iss'] != issuer: | |
return False, "Issuer mismatch" | |
# Get the default JWKS token. | |
jwks_keys = get_json(discovery['jwks_uri'])['keys'] | |
filtered_keys = [key for key in jwks_keys if key.get( | |
'kid') in 'default' or key.get('kid') is None] | |
key = None | |
if filtered_keys: | |
key = filtered_keys[0] | |
else: | |
return False, "No kid that is default or None" | |
# Now verify the given token | |
jwt_from_default = jwt.PyJWK(key) | |
header = jwt.get_unverified_header(token) | |
jwt.decode(token, jwt_from_default.key, [ | |
header["alg"]], options={'verify_signature': True}, audience=unvalidated_decoded_token['aud']) | |
return True, "JWT is valid" | |
except jwt.ExpiredSignatureError: | |
return False, "JWT has expired" | |
except jwt.InvalidIssuerError: | |
return False, "Invalid issuer" | |
except jwt.InvalidTokenError as e: | |
return False, f"Invalid token: {str(e)}" | |
except Exception as e: | |
return False, f"An error occurred: {str(e)}" | |
if __name__ == '__main__': | |
config = dotenv_values('.env') | |
discovery = None | |
# Discovery endpoint lookup | |
if config['discovery'] is not None and config['discovery'] != '': | |
discovery = get_json(config['discovery']) | |
# Compare issuer in configuration against well known issuer | |
if discovery['issuer'] != config['issuer']: | |
raise Exception('Issuer configuration mismatched') | |
# Validate a JWT against the known configured issuer | |
valid, reason = verify_jwt(config['token'], config['issuer'], discovery) | |
if valid: | |
print('Token is good! Extract the user info.') | |
else: | |
raise Exception(f'Issuer configuration mismatched. {reason}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pyjwt[crypto] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment