How to verify JWT id_token produced by MS Azure AD?
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.
For a JVM solution, using com.nimbusds:numbus-jose-jwt:4.29
is the most straight forward way to parse and validate a signed RSA256 id_token. The following Scala code parses the JWT token with a JSON Web Key:
val jwt = SignedJWT.parse(token)
val n = new Base64URL("Your Modulus Component of RSA Key")
val e = new Base64URL("AQAB")
val rsaKey = new RSAKey.Builder(n, e).keyUse(KeyUse.SIGNATURE).algorithm(JWSAlgorithm.RS256).build()
val verified = jwt.verify(new RSASSAVerifier(rsaKey))
Your application would still need to fetch the JSON Web Key Set from Azure Active Directory B2C discovery/v2.0/key
dynamically to get the set of keys potentially used by AAD B2C. This should probably be cached and have a TTL of not more than 24 hours for efficiency.