JWT Blacklisting via Memorystore

A commonly asked question on the Apigee community has been how can JWTs be revoked or blacklisting be applied. If you've already read Dino's interesting point of view on this topic and are still looking at implementing a solution, this example looks at implementing blacklisting using Google Cloud services - a Cloud Function implementation that is backed by Memorystore which provides a fully managed in-memory data store which we will use to track which ids are flagged.

Architecture

This solution does the following within the Apigee API Proxy

  1. Retrieves the Google Cloud Service Account credentials from the KVM. This service account has access to our cloud function that interacts with Cloud Memorystore.
  2. We get a token to access our Cloud Function using the process described here
    1. Generate a Signed JWT using the Service Account details.
    2. The JWT is exchanged for a token at the Google APIs OAuth2 endpoint via a Service Callout.
  3. Invoke the Cloud Function using our token.
    1. Cloud Function looks up the given id (In our case, our JWT Id) to see if it's flagged or to flagged one.
    2. We could then implement logic in our proxy based on the response of our Cloud Function ie specific logic based on whether or not the id is flagged or not.

Some important optimization notes on the above:

  1. The Cloud Function token can also be stored in the Apigee cache until it expires to minimize the Authentication requests.
  2. As part of configuring the Memorystore service for our use case, we need to ensure an appropriate memory eviction policy is set in the configuration. In this instance, I've set this to allkeys-lru to use least recently used key eviction. If this approach doesn't suit your use case and requires better control, the cloud function can be reimplemented to set id's to Memorystore with a TTL as well.

3. The example Cloud Function is implemented in Node.js using the redis package to interact with Memorystore.

I'm using the increment operation rather than set for flagging an id as it offers the highest performance according to the benchmarks provided, along with the exists operation.

The example cloud function code is available here.

Cloud Function Interface

The Cloud Function supports the following

  • GET with /{id}

This will return an empty response with 204 status code if the id has been blacklisted, otherwise a 404.

  • PUT with a request to /{id}

This will flag the given id and return a 201 status code if the id has been blacklisted, or 204 if it was already blacklisted.

Example

This API Proxy, and the cloud function source code can be found here. The API Proxy has been implemented with shared flows for getting a token from Google Cloud and calling the Cloud Function.

The example relies on encrypted KVM entries for the service account credentials and the url for the cloud function should be updated in the proxy.

Multi-region support

The above example suits a single region Apigee instance. For multi-region customers that are latency sensitive they would need to adapt the solution. For example, as Memorystore is not a multi-region solution, additional Memorystore instances would need to be configured in additional regions as required. The Cloud Function could then update all Memorystore instances simultaneously when an id is to be blacklisted and the lookup could then be done using the closest Cloud Function and Memorystore pair.

Comments
sidd-harth
Bronze 1
Bronze 1

It is a great article @dane knezic.

There was another community post from Dino talking about invalidating/blacklisting JWT tokens using Apigee Cache,

  • identify the token using a unique attribute like a jti
  • use a PopulateCache in /blacklist resource to store the token with its expires time
  • use LookUpCache & RaiseFault before using VerifyJWT in /verify resource.

I did not find the post, but attached is a sample implementation.

What are your views on using Memorystore and what pros does it bring when compared with Apigee Cache Policies?

jwt-invalidate-rev1-2020-02-20.zip

dknezic
Staff

Hi Sid

The cache doesn't provide consistency immediately which may be a requirement for this type of use case, and for a multi-region configuration a custom approach like this can give you more control.

Version history
Last update:
‎02-19-2020 11:55 PM
Updated by: