Verify and decode Azure Activity Directory Token

I wrote an article about [Azure Active Directory API v1.0 vs. v2.0] in May, 2019. In that article, I tried to verify an access token from Azure Activity Directory (AAD), and realized, if my Azure App has enabled Microsoft Graph API, a nounce will be added into my decoded access token. We can not easily verify this access token with the JsonWebToken Node.js package. In that article, I also suggested to use BeaererStrategy of Azure Active Directory Authentication Strategies using Node and Passportjs to verify your AD web token.

Let’s say, your access token doesn’t use Microsoft Graph API, you also want to verify the AD token. You understand the public key of Azure Active Directory lives at https://login.microsoftonline.com/common/discovery/keys.

It looks like this

{"keys": [{"kty": "RSA","use": "sig","kid": "ie_qWCXhXxt1zIEsu4c7acQVGn4","x5t": "ie_qWCXhXxt1zIEsu4c7acQVGn4","n": "68tx2cnOfkvHf705c-ZIZfXiyxE6c9LqxVQDjIs-9DbvDEI7453kZi9tvQYzskJFdBlD8MYuVhX8Bpi_YUFF8eoJ1PFC2_82sDhF-mNJ7sPrYsgMJAL3rfzOKQapx3m9RPS-18KAZOg-SIDwbNcrDm5rYw5oXi2jbO4ctKzRKP3jznvHDeLnLCTcnDGkMCRBuENJugwh7oHjtGph1L3vNpI2lroB3HK2TcD1Mr5cYTcrd-j6bmk8LRewLCJjipZwmu3DiW4_kRnigw8O5BmYgMYkWZ7Gl048KBZL-7PyHTAXb7tMp9FdJfWY-Xu9KB9ayAW84GbCMJ7qoojwoCDb3Q","e": "AQAB","x5c": [".....k"]},

Keep in mind that Microsoft rotates the public key periodically.

Based on the documentation of “How to Verify a JSON Web Token’s Signature using the JSON Web Key Set Endpoint”, I found it just didn’t work in that way. My step is:

1. Decode an access token at https://jwt.io/, and find the kid info

{
"typ": "JWT",
"alg": "RS256",
"x5t": "HBxl9mAe6gxavCkcoOU2THsDNa0",
"kid": "HBxl9mAe6gxavCkcoOU2THsDNa0"
}

2. Put the kid value as part of your http request header

3. Search your filtered JWKS for the key with the matching kid property.

4. Build a certificate using the corresponding x5c property in your JWKS.

5. Use the certificate to verify the JWT’s signature.

Here is the code:

var jwksClient = require('jwks-rsa');let client = jwksClient({  jwksUri: 'https://login.microsoftonline.com/common/discovery/keys'});client.getSigningKey(req.headers.kid, (err, key) => {  if (err !=null) {    console.log('err:' + err);  }  const signingKey = key.publicKey || key.rsaPublicKey;  console.log('signingKey:' + signingKey);  const decoded: any = jwt.verify(token, signingKey, { algorithms: ['RS256']});   console.log('decoded with signature verification: '+      JSON.stringify(decoded));})

Remember to attach a ‘kid’ to your http request header when you make a request

curl -X GET \
http://localhost:3000/api/user/ \
-H 'Accept: */*' \
-H 'Authorization: Bearer ey...A' \
-H 'Cache-Control: no-cache' \
-H 'Connection: keep-alive' \
-H 'Host: localhost:3000' \
-H 'kid: ie_qWCXhXxt1zIEsu4c7acQVGn4'

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store