apim
2019/07/24
 
24 Jul, 2019

WSO2 API Microgateway: Dealing with Revoked JWT Tokens

  • Fazlan Nazeem
  • Associate Technical Lead - WSO2

WSO2 API Micorgateway is a cloud native API Gateway which can be used to expose one or many microservices as APIs. It applies the common quality of service attributes on API requests such as security, rate limiting, and analytics; and also offers a wide range of features which help organizations to deploy APIs to microservice architectures efficiently.

It supports authentication and authorization for API requests in following forms:

  • OAuth2
  • Basic Auth
  • Mutual TLS

OAuth2

OAuth2 tokens can be self contained in JWT format or opaque by specification. WSO2 API Microgateway supports both of these formats:

  • Opaque (An external service validates the token)
  • Self contained in JWT format (The token itself is sufficient to check its validity)

Token Revocation

For opaque OAuth2 tokens, revocation is achieved by marking the token as invalid at the validation service (usually it is the STS). Since the gateway communicates with the validation service to authenticate a request, the gateway can be made aware of a revoked token as soon as it is revoked (assuming there is no caching). However, for self contained tokens, revocation is not straightforward due to the fact that there is no communication with an external service for validation. Hence the task becomes challenging.

When WSO2 Microgateway 2.6 was released back in September 2018, numerous questions were raised by the community regarding JWT token revocation. Back then, there was no direct solution for this and our suggestion was to mitigate the risk of a stolen token by using small expiry times during token generation. However, with the latest release of WSO2 Microgateway 3.0.1, we have introduced a solution. The product has been redesigned cleverly to facilitate a messaging system to revoke JWT tokens, starting from this release.

In this blog, we will discuss two mechanisms for token revocation and guide you through the steps needed to configure your environment to test this feature. You do not need any WSO2 specific components running apart from the microgateways for this.

Listed below are the two methods of token revocation introduced in this release:

  • Real-time non-persistent
  • Non-real-time persistent

WSO2 API Microgateway can be configured to enable either one or both methods at the same time. The following table illustrates the behavior of token revocation depending on whether each of them is enabled or disabled.

Real-time non-persistent Non real-time persistent Behavior
Enabled Disabled Token revocations will be reflected in real-time. If the microgateway is restarted, the revoked token will be treated as valid from there onward.
Disabled Enabled Token revocations will be persisted in etcd and will not be reflected in real-time. Microgateways will fetch revoked token information only during startup. Even if microgateways are restarted, the tokens will still be treated as invalid.
Enabled Enabled Token revocations will be reflected in real-time. Even if the microgateways are restarted, tokens will still be treated as invalid.

Real-time Non-persistent

This method, as suggested in the heading, is non-persistent. A revoked token will still continue to work if the microgateway is restarted since there is no involvement of a persistence mechanism.

Once a token is revoked at the STS, the STS is responsible to push a message to a JMS topic with the JTI of the revoked token. Microgateways will be listening to this topic and updating an in-memory map with the revoked token JTIs.

When a request reaches a microgateway, it checks whether the JTI of the token is in its in-memory map. If it exists, the requests will be blocked and otherwise, it will proceed to the backend. Since the in-memory information will be lost after a restart, a revoked token will be validated successfully upon a restart. In addition to this, a newly spawned microgateway will not be able to fetch the revoked token information since the topics are not durable - only the microgateways which have subscribed to the topic at the moment of revocation will receive the information.

Demo

Prerequisites

  1. WSO2 API Microgateway runtime with an API hosted in it.
  2. A valid JWT token to invoke the API mentioned in 1. This token can be generated via WSO2 API Manager’s token endpoint or an external STS of your choice. Make sure that you are able to invoke the API in the microgateway using this token.
  3. A JMS broker to host the token revocation topic.

Configure the broker

I'll be using Apache ActiveMQ 5.15.8 as the broker for this example. You can use any broker which supports the JMS specification. Do note that instructions may slightly change depending on the type of broker you use.

  1. Download Apache ActiveMQ 5.15.8 from here and extract it in the filesystem.
  2. Go to the bin directory and execute ‘./activemq start’. This will start activemq server and will listen in various ports for various protocols. The default TCP port is 61616.
  3. Open up your browser and head to https://localhost:8161/admin. This will present you the ActiveMQ web console which we will be using to create a topic. Use admin as the username and password for authentication.
  4. Click on ‘Topics’ on the top menu.
  5. A button will be available to create a topic with the provided name in the corresponding textfield. Create a topic with the name tokenRevocation.

Configure the microgateway to subscribe to the broker Topic

  1. Open micro-gw.conf file in the microgateway runtime.
  2. Scroll down to the tokenRevocationConfig.realtime section and change the configs as follows and save it. Notice the sections in bold which have been changed from the default configuration values. Also note that the following values are specific to ActiveMQ and will be different for any other broker type.
  3. [tokenRevocationConfig.realtime]
        enableRealtimeMessageRetrieval = true
        jmsConnectionTopic = "tokenRevocation"
        jmsConnectioninitialContextFactory = "org.apache.activemq.jndi.ActiveMQInitialContextFactory"
        jmsConnectionProviderUrl= "tcp://localhost:61616"
        jmsConnectionUsername = "admin"
        jmsConnectionPassword = "admin"
  4. Since we have configured ActiveMQ specific jmsConnectioninitialContextFactory we need to provide the relevant client implementation to the microgateway runtime. Therefore copy the following files from the ActiveMQ distribution in {ACTIVEMQ_HOME/}/lib to {MGW_HOME}/runtime/lib directory.
    • activemq-client-5.15.9.jar
    • geronimo-j2ee-management_1.1_spec-1.0.1.jar
  5. Start the microgateway by executing {MGW_HOME}/gateway . If all the configs are in place microgateway logs should print as follows during startup.
  6. INFO [wso2/gateway] - [TokenRevocationJMS] [-] subscriber service for token revocation is started

Testing by revoking a token

  1. Decode the JWT token you need to revoke and copy the value for the jti key in it.
  2. We will be interacting with the ActiveMQ REST API in order to push a message to the tokenRevocation topic. In order to revoke the above JWT token, execute the following curl command in a terminal:
  3. curl -X POST -d 'revokedToken=795c8a79-30ac-4a56-8474-7dd4f4d9551c&ttl=10000' "https://admin:admin@localhost:8161/api/message/tokenRevocation" -v
  4. Try executing the API in your microgateway with the JWT token corresponding to the above JTI. The API call should fail with an error as follows:
  5. {"fault":{"code":900901, "message":"Invalid Credentials", "description":"Invalid Credentials. Make sure you have given the correct access token"}}

Non Real-time Persistent

As opposed to the previous method, this mechanism involves persistence. This means that a revoked token will still continue to be blocked by all the microgateways, even across microgateway restarts. However, the revoked token information will be fetched by the gateways only during the startup. Therefore revocation is not real-time.

The STS is responsible to push a message with a suitable TTL (time to live) value to the etcd server upon a token revocation. Microgateways will fetch the revoked token JTIs from etcd on each startup. Once the TTL exceeds, etcd will automatically remove the corresponding tokens. By specifying a TTL value which slightly exceeds the expiry time of the token, etcd can be optimized to not store expired revoked tokens. In addition to the existing microgateways, if new microgateways are added to the deployment, all of them will still be able to fetch all revoked token information stored in etcd.

The default implementation for this uses etcd as the persistence layer to fetch data during the startup. If needed this can be customized to use a different method other than etcd. In such cases, the STS should push the message to the relevant persistence layer instead of etcd.

Demo

Prerequisites

  1. WSO2 API Microgateway runtime with an API hosted in it.
  2. A valid JWT token to invoke the API mentioned in 1. This token can be generated via WSO2 API Manager’s token endpoint or an external STS of your choice. Make sure that you are able to invoke the API in the microgateway using this token.
  3. Etcd server downloaded. For demo purposes we will be using etcd 3.3.13.
  4. Disable tokenRevocationConfig.realtime configs, if you have already enabled them when you tried out real-time revocation.

Configure etcd server

  1. Start the etcd server by executing the etcd command in the terminal. This will spin up a single node etcd server ready to serve requests via https://localhost:2379.
  2. Execute the following command in the terminal. This command creates a key value pair in etcd where the key is 25e0360d-e20b-44fd-b149-675243bbd994 and this value can be any string. The ttl is set to 3600s. This means after 1 hour etcd will automatically remove the key. Make sure you pass the correct JTI for the token you need to revoke (etcdctl is a command line client to interact with etcd service.)
  3. etcdctl mk 25e0360d-e20b-44fd-b149-675243bbd994  --ttl 3600
  4. Execute the following to check whether the above key has been saved in the server properly. This should print an output with the corresponding value for the key.
  5. etcdctl get 25e0360d-e20b-44fd-b149-675243bbd994

Configure the microgateway

  1. Open micro-gw.conf file in the microgateway runtime.
  2. Scroll down to the tokenRevocationConfig.persistent section and change the configurations as follows and save it. Notice the sections in bold which have been changed from the default configuration values. Keep the username and password fields empty as we have not enabled authentication with the etcd service.
  3. [tokenRevocationConfig.persistent]
          enablePersistentStorageRetrieval = true
          useDefault = true
          hostname = "https://127.0.0.1:2379/v2/keys"
          username = ""
          password = ""
  4. Start the microgateway. This should print a log as follows:
  5. INFO  [wso2/gateway] - [EtcdUtil] [-] One time ETCD revoked token retriever task initiated
  6. Try calling the microgateway API with the JWT token corresponding to the JTI which you revoked. API call should fail with the following error:
  7. {"fault":{"code":900901, "message":"Invalid Credentials", "description":"Invalid Credentials. Make sure you have given the correct access token"}}
  8. Restart the microgateway and perform the API call once more. Notice that the revoked token is being blocked even after the restart.
  9. Try generating a new JWT token and revoking it while the microgateway is running. Notice that API calls with the new token will continue to work until restarted. This is the reason why this method is ‘non-real-time’.

Summary

This blog discusses how WSO2 API Microgateway deals with revoked JWT tokens. There are mainly two ways to achieve this, but you can enable both modes for a hybrid approach if required. This will provide you with the capability to revoke a token in real-time and additionally to persist the revoked tokens so that this information will not be lost if the microgateways are restarted.

Learn more about WSO2 API Microgateway here.

Undefined