I heard someone asking today for support for Revocation of JWT, and I thought
about it a little, and decided I don’t see the point.
Specifically, I don’t see the point of the process described in this post regarding “Blacklisting JWT in express-jwt“. I believe that it’s possible to blacklist JWT. I just don’t see the point.
Let’s take a step back and look at OAuth
For those unaware, JWT refers to JSON Web Token, which is a type of token that can be used in APIs. The format of JWT is self-describing.
Here’s the key problem tokens address: how does a server decide whether to honor or reject a request? It’s a matter of authorization. OAuthV2 has been proposed and is now being used by the industry as the model or framework for enabling authorization in API-oriented apps. Basically it says, “give apps tokens, then grant access based on the token.”
Often the way things work under the OAuth framework is:
- an app running on a mobile phone connects to a token dispensary (a server) to request a token
- the server requires the client (==app) to provide some credentials before generating and dispensing a token. Sometimes the server also requires user authentication before token delivering a token. (This is done in the Authorization Code grant or the password grant.)
- the client app then sends this token to a different server to ask for services.
- the API server evaluates the token before granting service. Often this requires contacting the original token dispensary to see if the token is good, and to see if the token should be honored for the particular service being requested.
You can see there are three parties in the game: the app, the token dispensary, and the API server.
One handy optimization is to put the API endpoint behind an OAuth-aware proxy server, like Apigee Edge. (Disclaimer: I work for Apigee). The app then contacts Edge for a token (via POST /token). If the credentials are good, Edge generates and stores an opaque token, which looks like n06ztxcf2bRpN42cDwVUNvroGOO6tMdt, and delivers it back to the app. The app then requests service (via GET /service, or whatever), passing the previously obtained token. Edge sees this request, extracts the token within it, evaluates whether the token is good, and either passes the request through to the API endpoint or rejects it based on the token status.
The key thing: these tokens are opaque. The app doesn’t know what that token is, beyond a string of characters. The app cannot tell what the token is good for, unless it asks the token dispensary, which is the final arbiter. Sometimes when dispensing the token, the token dispensary also delivers metadata about the token, like: expiry, scopes, and other attributes. But that is not required, and not always done. So, bearer tokens are often opaque, and they are opaque by default in Apigee Edge.
And by “Bearer”, we mean… an app that possesses a token is presumed to “own” the token, and should be granted service based on that token alone. In other words, the token is a secret. It’s like cash money – if you lose it, someone else can spend it. But not exactly like cash. An opaque token is more like a promissory note or an IOU; to determine if it’s worth anything you need to go back to the issuing party, to ask “are you willing to pay up on this note?”
How is JWT different?
JWT is a different kind of OAuth token. OAuth is just a framework, and does not stipulate exactly the kind of token that needs to be generated and delivered. One type of token is the opaque bearer kind. JWT is an alternative format. Rather than being an opaque string, JWT is a self-describing format for bearer tokens. Generally, a JWT includes an encoded payload that can be decoded and read by anyone, and that payload contains a bunch of claims. The standard set of claims includes: when the token was generated (“issued at”), who generated it (the “issuer”), the intended audience, the expiry, and other things. JWT can include custom claims, such as “the user is a good person”. But more often the custom claim is: “this user is authorized to invoke /serviceA at endpoint http://example.com”, although this kind of claim is shortened quite a bit and is encoded in JSON, rather than in English.
Optionally accompanying that payload with its claims is a signature, which can be verified by any party possessing the public key used to sign it, (or, when using secret key encryption, the secret key). This is what is meant by “self describing”. The self-describing nature of JWT is the opposite of opaque. [JWT can be unsigned, can be signed, or can be encrypted. The encryption part is an optional part of the spec.]
(Commercial message: I said above that Apigee Edge generates opaque bearer tokens by default. You can also configure Apigee Edge to generate signed JWT.)
Why Self-describing Tokens?
The main benefit of a model that uses self-describing tokens is that the API endpoint need not contact the token dispensary in order to determine if the token is good, not-expired, and if a request bearing such a token ought to be honored. In other words, JWT supports federation. One party issues the token, another party can verify it, without contacting the issuer. Remember, JWT is a bearer model, which means the possessor of the token is presumed to be authorized to get service based on what’s in the token. This is truly like cash money this time, because … when honoring a JWT, the API endpoint need not contact the issuer, just as when accepting a $20 bill, you don’t have to contact the US Treasury to see if the bill is worth $20.
So how ’bout Revocation of JWT?
This is a long story and I’m finally getting to the point: If you want JWT with powers to revoke the token, then you abandon the federation benefit.
Making the JWT self-descrbing means no honoring party needs to contact the issuer. Just verify the signature (verify the $20 bill is real), and then grant service. If you add in revocation as a requirement, then the honoring party then needs to contact the issuer: “I have $20 bill with serial number T128-DCQ-2872JKDJ; should I honor it?”
It means a synchronous call across the two parties. Which means federation is effectively broken. You abandon the federation benefit.
The corollary to the above is that you also still incur all the overhead of the JWT handling – the signing and verification. So you get all the costs of JWT and none of the benefits.
If revocation of bearer tokens is important to you, you could do the same thing with an opaque bearer token and eliminate all the fussy signature and validation stuff.
When you’re using an API Proxy server like Apigee Edge for both issuing and verifying+validating tokens, then there is no expensive additional remote call to check the revocation status. But you still lack the federation benefit, and you still incur this signing and verification nonsense.
I think when people ask for the ability to handle JWT with revocation, they don’t really understand what they’re asking.
Guillaume Berche 2015-September-7
[Typo fixed]
Thanks for this great blog. Can you please help me find the apigee documentation which describe how to configure apigee to generate signed JWT access tokens, and how apigee would support resource servers (api endpoints) verifying the signed JWT access tokens (i.e sharing secret or public /private keys) ?
dpc 2015-September-7
Hi Guillaume,
First, In general with questions like this about Apigee’s products, I suggest that you post to http://community.apigee.com, which is the Q-and-A forum monitored by all the Apigeeks.
Specific to this question though, I have build an Apigee Edge callout in Java which does Verification of JWT. And also another which does generation of JWT. The verifier can verify signatures either done in HMAC or with RSA public/private key pairs, and can verify the presence of specific custom or standard claims in the JWT. Once the JWT is verified, Apigee Edge could inject a header into the request before proxying it to the backend api resource. The header could be something as simple as “approved” or it could be a compound value with elaborations from the JWT contained within it. An Apigee technical field person can show you all of this.
dpc 2018-March-18
Update. Here’s the link to the documentation of the built-in policies:
https://docs.apigee.com/api-platform/reference/policies/jwt-policies-overview
Denis 2015-September-7
Exactly my thoughts.
If you are gonna be revoking or blacklisting JWT tokens most of the time you could just use normal opaque tokens with same functionality but less complexity.
The only benefit of JWT tokens in this scenario is that you could check validity and expiry of token before hitting the Auth server.
Daniel Campagnoli 2016-January-18
I’m looking at implementing a JWT token service soon and had been thinking about this. My thought was as the blacklist probably won’t be that big or updating too often then the auth service could publish a bloom filter of blacklisted jti’s. Services would keep a copy of the bloom filter to minimize requests to the auth service to check the validity of the token.
Peter Schulz 2016-May-2
Good post.
I think there is another angle to the revocation problem.
I believe the easiest way to handle this “issue” is simply issuing tokens with (short) expiration periods. People asking for revocation must be attempting to issue indefinitely valid tokens, or expiration periods in terms of years, like the way you issue SSL certificates. For me such a use borders on misuse of JWT. They are for authenticating on the web – you log in, you do some actions, you are gone (and so is the token!). It’s fast and short and that’s what JWT is IMHO really meant for.
Used that way the question of “revocation” does not even enter the picture.
dpc 2016-May-6
YES! Exactly right. Short-lived tokens are the best tokens. How short is short? Probably 30-60 minutes. Refreshing a token should be pretty easy and painless for the user.
Dragan 2018-February-1
great post/blog and the logic behind it is unbearable :).
But re: short-tokens and revoking, short-tokens don’t really solve the revocation IMO.
(a) typically if you’re using it to protect the ‘non-expensive’ resource like some web site info, token lifetime is mimicking that of the login session, and for that you rarely need to revoke it (sometimes, yes, true), i.e. you just let that user access for a bit longer and you don’t care (if it’s not too long expiration).
(b) if you have something that could be described as ‘expensive’ resource, like form of API access that is calculated and based in ‘seconds’ (e.g. Google Cloud API, I believe it’s in 15-secs). Then 60-mins is too much. As you’re incurring a bill to yourself if you’re letting the token expire. And in that case going for shorter expiration like 5, 10 mins is probably too frequent (for various reasons).
What I’m trying to say is, short lived JWT tokens are not really a solution for revocation, most of the time (in my experience), so just use opaque tokens, as you’ve been saying all along.
Marc Busqué 2016-November-3
Thanks for this great post. I agree with most part of it.
However, I still think there is a point in JWT revocation from a security point of view.
First of all, JWT does not only apply to OAuth. You can have a custom authentication system and still use JWT. But, as you said, talking about OAuth, and being Authorization Server and Resource Server different servers (which it is not always the case), you loose the federation benefit. In other scenarios, maintaining a blacklist or some other revocation mechanism, you also loose the benefit of stateless tokens.
However, as I said, I see some security benefits in using JWT and being able to revoke them. Opaque tokens have some security concerns related with the fact they are persisted in the server, just like passwords: the need to store them hashed, timing attacks, an expiration mechanism… This is not the case for JWT, even if you store its `jti` claim in a blacklist (alone, it is useless information).
If third party clients are accessing a server that uses JWT, it is unacceptable to trust those clients will do the right job and destroy tokens from its store. A lot of times, using short lived tokens is not usable. But, again, I think that in these scenarios, using JWT with an stateful revocation system, even with its looses, have security benefits.
Marc Busqué 2016-November-3
Well, doing further research I see that JWT could be sensitive to timing attack just like with any opaque token. The comparison exists anyway… I got confused. Still you gain not having to hash anything and, usually with current libraries, expiration time management for free. But it is one point less for it in my personal scoreboard 🙂
dpc 2016-December-28
Absolutely right! JWT are useful in scenarios outside of OAuth.
Rather than focusing on how to revoke JWT, I think it might be better to just enforce short-lived JWT, and focus on revoking the clients, if necessary.
But that’s just my perspective.
jsonwebtoken 2017-January-2
hey.. the module below lets you authenticate JWT based on IP of incoming request. is this any useful?
https://www.npmjs.com/package/express-jwt-ip
dpc 2017-February-6
I haven’t seen the module, but yes, sure! that’s a possibly valuable approval mechanism or restriction.
For example, a client runs on a particular IP address. It obtains a JWT, and then presents it back when requesting service. When checking the token, the service side can verify the IP claim in the JWT against the actual IP on the inbound request. That means if the JWT gets swiped, it cannot be used from a different IP address. so, yes, could be very useful.
T 2017-May-13
That’s a valid use case for a stable connection, not of use for mobile where jwts apparently are more suited to be. Think of 4g connections while moving, IP addresses change frequently.
Evan 2017-March-9
Thanks for the post: found your analogies really apt for the problem at hand.
However, I still have a question with regards to microservices and gateways, because it still seems to me that you can have a gatekeeper who receives opaque tokens and returns associated JWTs (if present), while the microservices behind the gatekeeper can remain federated with just JWTs. Is that a valid approach? Seems that it would offer revocation capability on *access* to already issued JWTs, while the JWTs inside the walled-garden still provide federation.
My own attempt at an analogy: ever been to a wine expo? Usually they’re hosted in big warehouses, and you have to buy a ticket to get access. At the door, the doorman exchanges your ticket for a bracelet. Inside the warehouse, your bracelet gets to access to all the booths inside, with some booths giving you more or less depending on the color of your bracelet. I see that as akin to the doorman asking “I have $20 bill with serial number T128-DCQ-2872JKDJ; should I honor it?”, while all the wine stalls within are “just verify[ing] the signature … [that] the $20 bill is real”.
My thinking is something like that would be done with a proxy (nginx or similar) attached to a Redis db (or similar) that would translate opaque tokens to JWT and vice versa, depending on traffic was inbound or outbound. Revocation would then just be deleting a record in Redis so that you couldn’t look up a JWT when given an opaque token.
What are you thoughts about that approach? Am I still undoing some benefit to JWTs that I’m not aware of? Is there some other pitfall I’m not aware of? Would really appreciate your thoughts!
Doubting JWTer 2017-April-24
I’m having a hard time seeing when anything but few-second (or less) JWT tokens would be appropriate for user-facing applications, and then only to pre-authenticate to another app that will issue its own session. Let me explain.
First, you would never use a non-revokable token as a client-side authentication mechanism, because that would mean that someone could get back onto a system after the related end user explicitly “signed out” or was “kicked” by an administrator. That pretty much rules out using JWTs in place of traditional (often cookie-based) sessions.
Second, let’s say you dialed down the lifetime of your non-revokable tokens (say from 30 minutes to 5 minutes) to close up the exposure window after sign-out/kick (when there might still be valid tokens in the wild). In practice, you’d end up dialing these down to a few seconds (to “near real time”) if an auditor started poking around, meaning that your load on the authentication service would increase and client-side performance could suffer (requesting/waiting for a fresh token every few seconds).
So…that pretty much leaves one user-facing use case for authenticated JWT sessions: the occasional transfer of an authenticated user from one system to another that trusts the original system. (Yes, that a federation use case, but then I’d expect the receiving system to issue its own sessions and ignore JWT for the duration of the session.)
Andrey 2017-June-28
I’m new in microservices/gateways. Have my point of view and would be interesting to get any feedback.
Client sends login request through API Gateway to Auth Service where JWT token generates with IP address and some user info. Then API Gateway caches this data (for ex. Redis) and returns to the client. Next client requests verified by API Gateway (check IP Address, check if token is in cache and not expired). Then it can generate another token for microservices or just use the same one. If user logouts, banned, blacklisted or whatever, API Gateway deletes token from cache and token becomes invalidated.
Alex 2020-September-13
Looks perfect. Especially the part when you save the ip in token