pythonazurejwtadal

How to verify JWT id_token produced by MS Azure AD?


I have an angularjs SPA web app which uses ADAL-JS (and adal-angular). It's set up to authenticate vs our corporate AD in MS Azure. The log-in flow seems to work correctly, and the SPA receives an id_token.

Next, when the user clicks a button, the SPA makes a request to a REST API I am hosting on AWS API Gateway. I am passing the id_token on the Authorization: Bearer <id_token> header. The API Gateway receives the header as intended, and now has to determine if the given token is good or not to either allow or deny access.

I have a sample token, and it parses correctly on https://jwt.io/ but I have so far failed to find the Public Key or Certificate I should use to verify the signature. I have looked in:

I think I should use the value of the x5c property of the key in https://login.microsoftonline.com/common/discovery/keys matching the kid and x5t properties from the JWT id_token (currently a3QN0BZS7s4nN-BdrjbF0Y_LdMM, which leads to an x5c value starting with "MIIDBTCCAe2gAwIBAgIQY..." ). However, the https://jwt.io/ page reports "Invalid Signature" (I also tried wrapping the key value with "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----").

Also, is there a (possibly python) library that can facilitate the verification of a given id_token as in the case above (so that I won't have to go grab the signing key on the fly myself?)... The best I could find (ADAL for python) doesn't seem to provide this feature?


Solution

  • The best solution I could put together so far:

    Grab the certificate (the first value in the x5c property array) from either https://login.microsoftonline.com/common/discovery/keys or https://login.microsoftonline.com/common/discovery/v2.0/keys, matching kid and x5t from the id_token.

    Wrap the certificate in -----BEGIN CERTIFICATE-----\n and \n-----END CERTIFICATE----- (the newlines seem to matter), and use the result as Public Key (in conjunction with the id_token, on https://jwt.io/ ).

    Of course, your actual use case will likely be to have some program validate the incoming JWT id_tokens, so your goal won't be to simply get the token to validate through the web UI on https://jwt.io/.

    For instance, in python, I need something like this:

    #!/usr/bin/env python
    
    import jwt
    from cryptography.x509 import load_pem_x509_certificate
    from cryptography.hazmat.backends import default_backend
    
    PEMSTART = "-----BEGIN CERTIFICATE-----\n"
    PEMEND = "\n-----END CERTIFICATE-----\n"
    
    mspubkey = "The value from the x5c property"
    IDTOKEN = "the id_token to be validated"
    tenant_id = "your tenant id"
    
    cert_str = PEMSTART + mspubkey + PEMEND
    cert_obj = load_pem_x509_certificate(cert_str, default_backend())
    public_key = cert_obj.public_key()
    
    decoded = jwt.decode(IDTOKEN, public_key, algorithms=['RS256'], audience=tenant_id)
    if decoded:
        print "Decoded!"
    else:
        print "Could not decode token."
    

    For a list of JWT libraries in various languages, see the JWT Site. I'm using pyjwt, and its cryptography dependency (which has binary dependencies, so needs to be built and packaged for the target OS).

    And then, of course, you can verify additional details such as the claims as recommended here.