How can I revoke a JWT token?

I am using Spring Security OAuth2 and JWT tokens. My question is: How can I revoke a JWT token?

As mentioned here http://projects.spring.io/spring-security-oauth/docs/oauth2.html, revocation is done by refresh token. But it does not seem to work.


Solution 1:

In general the easiest answer would be to say that you cannot revoke a JWT token, but that's simply not true. The honest answer is that the cost of supporting JWT revocation is sufficiently big for not being worth most of the times or plainly reconsider an alternative to JWT.

Having said that, in some scenarios you might need both JWT and immediate token revocation so lets go through what it would take, but first we'll cover some concepts.

JWT (Learn JSON Web Tokens) just specifies a token format, this revocation problem would also apply to any format used in what's usually known as a self-contained or by-value token. I like the latter terminology, because it makes a good contrast with by-reference tokens.

by-value token - associated information, including token lifetime, is contained in the token itself and the information can be verified as originating from a trusted source (digital signatures to the rescue)

by-reference token - associated information is kept on server-side storage that is then obtained using the token value as the key; being server-side storage the associated information is implicitly trusted

Before the JWT Big Bang we already dealt with tokens in our authentication systems; it was common for an application to create a session identifier upon user login that would then be used so that the user did not had to repeat the login process each time. These session identifiers were used as key indexes for server-side storage and if this sounds similar to something you recently read, you're right, this indeed classifies as a by-reference token.

Using the same analogy, understanding revocation for by-reference tokens is trivial; we just delete the server-side storage mapped to that key and the next time the key is provided it will be invalid.

For by-value tokens we just need to implement the opposite. When you request the revocation of the token you store something that allows you to uniquely identify that token so that next time you receive it you can additionally check if it was revoked. If you're already thinking that something like this will not scale, have in mind that you only need to store the data until the time the token would expire and in most cases you could probably just store an hash of the token so it would always be something of a known size.

As a last note and to center this on OAuth 2.0, the revocation of by-value access tokens is currently not standardized. Nonetheless, the OAuth 2.0 Token revocation specifically states that it can still be achieved as long as both the authorization server and resource server agree to a custom way of handling this:

In the former case (self-contained tokens), some (currently non-standardized) backend interaction between the authorization server and the resource server may be used when immediate access token revocation is desired.

If you control both the authorization server and resource server this is very easy to achieve. On the other hand if you delegate the authorization server role to a cloud provider like Auth0 or a third-party component like Spring OAuth 2.0 you most likely need to approach things differently as you'll probably only get what's already standardized.

An interesting reference

This article explain a another way to do that: Blacklist JWT It contains some interesting pratices and pattern followed by RFC7523

Solution 2:

The JWT cann't be revoked.

But here is the a alternative solution called as JWT old for new exchange schema.

Because we can’t invalidate the issued token before expire time, we always use short-time token, such as 30 minute. When the token expired, we use the old token exchange a new token. The critical point is one old token can exchange one new token only.

In center auth server, we maintain a table like this:

table auth_tokens(
    user_id,
    jwt_hash,
    expire
)

user_id contained in JWT string. jwt_hash is a hash value of whole JWT string,Such as SHA256. expire field is optional.

The following is work flow:

  1. User request the login API with username and password, the auth server issue one token, and register the token ( add one row in the table. )
  2. When the token expired, user request the exchange API with the old token. Firstly the auth server validate the old token as normal except expire checking, then create the token hash value, then lookup above table by user id:
    • If found record and user_id and jwt_hash is match, then issue new token and update the table.
    • If found record, but user_id and jwt_hash is not match , it means someone has use the token exchanged new token before. The token be hacked, delete records by user_id and response with alert information.
    • if not found record, user need login again or only input password.
  3. when use changed the password or login out, delete record by user id.

To use token continuously ,both legal user and hacker need exchange new token continuously, but only one can succeed, when one fails, both need to login again at next exchange time.

So if hacker got the token, it can be used for a short time, but can't exchange for a new one if a legal user exchanged new one next time, because the token validity period is short. It is more secure this way.

If there is no hacker, normal user also need exchange new token periodically ,such as every 30 minutes, this is just like login automatically. The extra load is not high and we can adjust expire time for our application.

source: http://www.jianshu.com/p/b11accc40ba7

Solution 3:

This doesn't exactly answer you question in regards to the Spring framework, but here's an article that talks about why if you need the ability to revoke JWT's, you might not want to go with JWT's in the first place, and instead use regular, opaque Bearer tokens.

https://www.dinochiesa.net/?p=1388

Solution 4:

One way to revoke a JWT is by leveraging a distributed event system that notifies services when refresh tokens have been revoked. The identity provider broadcasts an event when a refresh token is revoked and other backends/services listen for the event. When an event is received the backends/services update a local cache that maintains a set of users whose refresh tokens have been revoked.

This cache is then checked whenever a JWT is verified to determine if the JWT should be revoked or not. This is all based on the duration of JWTs and expiration instant of individual JWTs.

This article, Revoking JWTs, illustrates this concept and has a sample app on Github.