Last active
December 10, 2018 20:24
-
-
Save tmpapageorgiou/0c1188283f5ee450b517ffe2f8912c98 to your computer and use it in GitHub Desktop.
Using modulus and expoent directly to validate azure ad jwt token
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 jwt | |
import requests | |
import base64 | |
from jose.utils import base64url_decode | |
from cryptography.x509 import load_pem_x509_certificate | |
from cryptography.hazmat.backends import default_backend | |
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers | |
from cryptography.hazmat.primitives import serialization | |
def ensure_bytes(key): | |
if isinstance(key, str): | |
key = key.encode('utf-8') | |
return str(key) | |
def decode_value(val): | |
decoded = base64url_decode(ensure_bytes(val)) | |
return int(decoded.encode('hex'), 16) | |
def rsa_pem_from_jwk(jwk): | |
return RSAPublicNumbers( | |
n=decode_value(jwk['n']), | |
e=decode_value(jwk['e']) | |
).public_key(default_backend()).public_bytes( | |
encoding=serialization.Encoding.PEM, | |
format=serialization.PublicFormat.SubjectPublicKeyInfo | |
) | |
class InvalidAuthorizationToken(Exception): | |
def __init__(self, details): | |
super().__init__('Invalid authorization token: ' + details) | |
def get_kid(token): | |
headers = jwt.get_unverified_header(token) | |
if not headers: | |
raise InvalidAuthorizationToken('missing headers') | |
try: | |
return headers['kid'] | |
except KeyError: | |
raise InvalidAuthorizationToken('missing kid') | |
def get_jwk(token): | |
kid = get_kid(token) | |
jwks = get_jwks(token) | |
for jwk in jwks.get('keys'): | |
if jwk.get('kid') == kid: | |
return jwk | |
raise InvalidAuthorizationToken('kid not recognized') | |
def get_public_key(token): | |
return rsa_pem_from_jwk(get_jwk(token)) | |
def validate_jwt(jwt_to_validate): | |
public_key = get_public_key(jwt_to_validate) | |
options = {'verify_aud': False, 'verify_exp': False} | |
import ipdb; ipdb.set_trace() # BREAKPOINT | |
decoded = jwt.decode(jwt_to_validate, | |
public_key, | |
verify=True, | |
algorithms=['RS256'], | |
audience=valid_audiences, | |
options=options) | |
# do what you wish with decoded token: | |
# if we get here, the JWT is validated | |
print(decoded) | |
def main(): | |
import sys | |
import traceback | |
if len(sys.argv) < 2: | |
print('Please provide a JWT as script argument') | |
return | |
jwt = sys.argv[1] | |
if not jwt: | |
print('Please pass a valid JWT') | |
try: | |
validate_jwt(jwt) | |
except Exception as ex: | |
traceback.print_exc() | |
print('The JWT is not valid!') | |
else: | |
print('The JWT is valid!') | |
def get_jwks(token): | |
token_header = jwt.get_unverified_header(token) | |
res = requests.get('https://login.microsoftonline.com/common/.well-known/openid-configuration') | |
jwk_uri = res.json()['jwks_uri'] | |
res = requests.get(jwk_uri) | |
return res.json() | |
valid_audiences = 'https://graph.microsoft.com' | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment