Many developers are familiar with JWT Tokens (the “J” here is not from Java at all, but from the generally accepted JSON), and some have been working with them for a long time.
There is a wonderful article about working with JWT on GitHub
I will share my ideas on how to solve the problems with Revoked and Sign Out Tokens. The problems with working with such tokens for 2021 are well-known and make working with the JWT from stateless to stateful when we want to produce revoke, sign out and refresh token functionality. Security maintaining is often more important than the performance of stateless JWT. And in this case we need to choose between compromises or hard ways.
So, let's imagine we have two microservices:
- Gateway - accepts all requests to the cluster, does their initial processing and redirects to specific microservices
- Accounts - stores user authentication data
At the moment when the Gateway receives the next request from the client, the following actions take place:
- Client API-Key is checking (including client platform, site domain)
- Gateway is checking the JWT token for its presence in the Revoked, Sign Out list (if it is not a request to Refresh the token)
- Gateway parses the JWT token, gets the UserId, Email / Login from it, and put them to the Header of the request for transmission to the microservice
The implementation of the second point makes the check of each stateful request, and leads to decreased performance under high load. Moreover, verification can be implemented in the following ways:
- Canonically “correct” for a microservice architecture, where the functionality of the microservices distributed among points of responsibility. In this case, the Gateway microservice must contact the Accounts microservice to validate the JWT Tokens. This will have to be done synchronous (using REST requests between services), and this can lead to a decreased performance for high load services (but for some cases this may be suitable)
- By providing the Gateway microservice access to the Accounts microservice data source. This is some violation of the canonical, unspoken rules when working with microservices. However, in this case, time saved for message exchange synchronous (asynchronous cannot be done because the connection with the client could be broken)
Suppose we choose the second method in order to increase performance the verification of tokens. There is still a way of working with a token called stateful.
Since the nature of Revoked and Sign Out Tokens is such that their lifetime can be quite short (good practice), it is not necessary to store them directly in the relational DB. To do this, you can use NoSQL databases, for example, HBase, Redis, Cassandra, etc. Then, access to storage in RAM can be much faster than to a database on HDD / SSD SASS / SSD NVMe.
When storing Refresh Tokens, it is still better to use a relational DB, because they are accessed much less frequently than when checking the validity of Access Tokens from JWT.