In Memory Token Validation

Background

Previously we covered migrating our SPA Code Sample to Azure AD. This article drills into Open ID Connect features that can be used when validating access tokens in memory.

First we will do some Manual Access Token Signature Validation using online tools and then we will look at the equivalent code.

View the the Access Token’s Key Identifier

Run this blog’s Azure Code Sample or your own application and use an HTTP debugger to get an Access Token. Then use the JWT.IO Page, copy in the token and get the value of the ‘kid‘ parameter in the JWT header:

Initially the token is seen to have an Invalid Signature. This is because we need to provide the Token Signing Public Key in the below text box:

If there is a nonce field in the JWT’s header then it is intended only for Microsoft developed Azure APIs . These tokens require special handling and will always fail standards based validation.

Run a Metadata Lookup and get the JWKS Endpoint

For my Azure AD Tenant, the Open Id Connect metadata URL is as follows:

We can see that the endpoint for downloading token signing (JWKS) keys is at the following location:

Download Token Signing Keys

Next get JWKS keys from the endpoint we located, and find the x5c value that matches the access tokens’ kid value:

For Azure AD the x5c value is the public key of the Microsoft Token Signing Certificate.

Save the Key in Certificate (PEM) Format

This involves simply surrounding the key with these well known lines:

If we save the text to file we can view it using the operating system viewer:

Paste the Key into JWT.IO

On the website, paste the above long key into the Verify Signature text box and we should now see that the Token’s Signature is Verified.

In my usage the Microsoft certificate was not from a Trusted Issuer, and this could potentially cause signature verification issues, depending on which Third Party Security Library you use.

Full Access Token Validation

As we covered in Step 12 of our Original Code Sample Workflow, the overall technical validation of received access tokens requires these checks:

Property Expectation
Issuer The token is from Azure AD and from our tenant
Audience The token was issued for our API
Digital Signature The token has not been tampered with since issued
Active The token has not expired

Once complete, your API may also want to apply extra security checks, such as checking for a recognized calling application. Your code would then move on to applying business level authorization rules.

API Token Validation Code

In my API’s Authenticator Class I wrote the validation code as follows, which follows the same steps as the above manual validation.

At runtime I first decode the JWT and read its Key Identifier, and we then need to get the corresponding Token Signing Public Key.

I used a third party security library called jwks-rsa to do this, since getting the key requires some low level security knowledge. Most of us would make mistakes if we tried to write this code ourselves:

I used another library called jsonwebtoken to do the actual validation, and called it as follows, supplying the expected issuer and audience. Note that our API gets the Issuer Id from downloaded metadata:

We can easily test the error cases of supplying invalid values for issuer and audience. We can also supply an expired access token, in which case the library throws an exception and our API logs the returned error:

Since the above operation is expensive I continued to use the Claims Caching technique presented earlier, so that it only occurs when a server receives a new token for the first time, rather than on every single API call.

Other Technology Stacks

My sample API is in Node JS, but once you understand concepts you should be able to code it in any technology. Note that the JWT.IO web site points to recommended libraries for multiple technology stacks:

Here is some online code that may be useful to some of you:

Where Are We?

We have covered a variation where our API used In Memory Access Token Validation, though I would have preferred to externalize this code.

Next we will cover a different variation, where the API is implemented in a non Javascript server side language.

Next Steps