You cannot invalidate JWT tokens
This is simple not true. You ALWAYS will sign your tokens with a well known secret, you could eventually even add some salt from a database to it.
I think one of the benefits of some (most?) JWT implementations is that it doesn't hit the database on every request - the token is only validated against a global secret.So you can't invalidate a single user's session without invalidating all users' sessions.
If a user changes their password, their roles change, etc, then the counter gets incremented so all tokens issued up to that point won't be valid anymore.
These are just some random and half-baked thoughts, I have no idea what OP does, but there are options to limit hitting backing DBs anyway.
Just as if you were storing state on the server, it still requires a means of propagating the user's state to every server. But we only need to propagate a single ID once per session (at logout) instead of propagating all the session data to all the servers on every request.
Another approach is what I call the "subject epoch" pattern. The subject of a token (the "sub" JWT claim) is often something like a user ID. When an event occurs that requires all tokens for a given subject to be revoked, save that time stamp as the subject's "epoch". When processing JWTs, those issued before the subject's epoch must be considered invalid.
Is there a way around this? I like the idea of a revocation list, but this seems to be a pretty big concern.
If we used a different approach, then the same error (losing the data that's being synched) would result in losing all of the customer sessions.
JWTs generally contain an expiration timestamp.
Incidentally, this limitation isn't too surprising to me... Is there any possible token-based authentication scheme that is both stateless (ie. no round trip to the database on every call) AND invalidate-able? Seems like any form of invalidation would require storing the "is valid" state somewhere else...
I suspect this is provably impossible.
There are all sorts of things you can do at the protocol/platform level if you have a shared secret, but with only the constraints of an open authentication scheme you lack the tools to do this.
CRLs of course introduce lots of replication complexity and timing bounds to consider, and you probably want to pair them with short lived tokens to keep the CRL size manageable. (and then delve into refresh tokens)
As the OP points at, you most likely don't need any of this.
Our current Implementation is:
- Really short lived JWT of 1 minute - If the JWT is invalid and the user didn't do a request in the last minute it does query the database for a session token (we use session tokens and jwt). if the Token is inside the database/redis/ehcache/whatever the user gets still a new JWT token.
Actually we did that since we needed a "sane" way of revoking tokens fast but still keep the user logged in until the browser is closed.
We don't have a mobile client (yet) but I guess we try to do something like that, too. just not with a session. This works really well and mostly our users won't hang around for more than a minute and when they do its not a problem to have a single backend call.
AFAIK, Stateless is about independent request/response and not needing a server to retain session information through the course of multiple requests. It has nothing to do with whether or not you're checking a database to cross-reference credentials - and I wouldn't keep anything more than an ID/name in a JWT... ever.
Practically speaking, we use a bit of metadata (jsonb documents in pgsql, mostly) for each JWT-issuing party, which describes how to validate principals, how to map incoming claims to "our" claims (e.g., "by what name does sts.acme.com call a 'role'? is sts.acme.com allowed to vouch for the 'admin' role?) in addition to the more central things like shared secrets, certificates, etc. This kind of partial trust is how claims auth is supposed to work, and avoids any unnecessary provisioning/syncing of user details.
SPKI (RFC 2692/2693) solved this in 1999, with its timed CRLs. Exactly one CRL is valid for any given period of time: when a token (= certificate) is received, it is invalid if it is referenced in the CRL, and valid otherwise.
The approach I use is to replace the shared session store (which is large, grows linearly with your user base, and hard to sync across clusters if you need that) with a shared revocation list.
The revocation list is small, easy to check, only contains IDs and a TTL, much less cumbersome to replicate... And is functionally equivalent.
If you have already gone the route of using a token for your session, it's really easy to implement and scales much better than a massive in memory store.
The difference in speed between checking for a token in the big pool (all tokens) vs the small pool (revoked tokens) should be a lot smaller than the round trip to token server (i.e. network cost).
Also, you should be able to easily store millions of tokens on a single server (something like Redis). Do you really have a site that has multiple millions of people logged in?
Depending on your situation, managing a revocation list in memory at each edge node might be feasible, reducing the overhead of a network call to a per-cluster store, which is another advantage of using a short revocation list instead of a large session store that can't feasibly be shared like that.
(Edit: looking back at my comment, I shouldn't have said a "shared" revocation list, but a distributed one. Whoops!)
Actually most people should take their authentication mechanism as something really really important and not just rely on something. It's really important that your authentication/authorization stuff works and is hardened, not just for you, also for your users. I know that auth could be really hard, especially when the language of your choice contains some bugs, but its a nogo to just use something when it is that important. I don't mean that you can't use a library, but if you do please double check the code twice on each version.
* Store the invalidated tokens in some fast store, in memory store and check that before accepting tokens.
* Have auth tokens be very short lived (e.g., 1 minute) and require the use of a "refresh token" to get a new auth token when it's expired. This is the mechanism used by OAuth 2 (which strangely is never mentioned in the article). The refresh tokens should be random strings stored in a db, and they can be simply deleted on invalidation.
You ALWAYS will sign your tokens with a well known secret, you could eventually even add some salt from a database to it.
Exactly. Each JWT I generate has a salt inside which I store in the DB, and ideally the salt has accompanying data such as the datetime the JWT was generated, the IP address that generated it, etc. Now you can provide an interface that lists "sessions" and allows the invalidation of logged in sessions by deleting the salt from the DB, rendering the JWT invalid. - generate the salt
- generate the jwt
- use the header base64 string as the key for your database
- store the salt with the header base64 as the key in your database
maybe?just one idea, basically when you are > 100.000 users you barely invalidate a single token. mostly you would kill of your secret which will invalidate any token anyway. but there are numerous ways of doing so.
Edit:
to the poster after me:
actually we never needed to logout a single user. but actually our authorization is not stored in our JWT so if we want to kill a user off, we could just remove any rights from him so he gets a site where he needs to either logout or refresh (which will show him the same site again).
If you need to sign out a user across all logged-in sessions, use a revocation list based on UID, rather than token-id.
Anyone know any good articles I can read on the topic?
Whether it is good to bind the JWT to some combination of user-agent, device cookie, IP address (not recommended) or whatever, is a separate topic...
Each JWT contains two pieces of info: user ID, and salt. This stops the JWT from being a deterministic token that is always the same each time a token is generated for a specific user.
It also allows invalidation. On each request a middleware checks validity of the salt from the JWT. A Redis cache layer here makes this a 1-2ms operation and easy to scale. If you need to invalidate a JWT delete a salt and the respective token is now invalid. Or delete all salts for a user to invalidate all their sessions.
Honestly this use of a JWT is basically just a lightweight session that passes the session ID via a technique that isn't insecure by default like cookies are. Rather than having to opt in to CSRF protection your stuff is hard to CSRF unless your application persists the token insecurely.
Why not just store your sessionID in a cryptographically signed HttpOnly cookie? In most use cases, it'd be less ambiguous, better protected from JS attacks, and equal-or-less vulnerable to CSRF.
This refers to invalidating individual tokens. I've just updated the title and text in the post to clarify that.
> Compared to what? Actually JWT will have the same secureness like Bearer Tokens or Cookies, wherever you store it, its not `less` secure. Horrible article points here.
Compared to storing something in a cookie. The article explicitly states that this concerns JWT tokens that are not stored in a cookie. And comparing "JWT vs. Cookies" makes absolutely no sense, they're different kinds of things.
> They take the same amount of storage than signed cookies, wuhu I use JWT so often and I NEVER exceeded the cookie limit, but I wouldn't store them there anyway.
Again - the article explicitly states that this refers to stateless tokens, ie. where the data is contained in the token itself. Did you actually read the full article, or just the headers?
> JWT is as secure as any other solution and this article proves it since he isn't well informed, he just throws a bunch of stuff out and tries to be "an expert". Btw. every other solution is really dangerous as well, especially Cookies could be a total mess of security problems. The same could actually happen with Bearer Tokens.
Personal attack and hand-wavy statements without ever actually addressing any specific security concerns. Take that elsewhere. I'm here for technical discussion, not for throwing shit.
> My toughts are use the thing your most happy with and the thing you understand
No, that is not how security works.
> and the thing you can implement with code that is totally clear and especially code that is really really easy to understand. And actually JWT will mostly win this battle.
No, it won't. The typical framework will only require you to load a single plugin to get sessions working, and literally nothing else. JWT tokens require manual handling.
> I've seen a lot of people just using Session Cookies from their Framework, but eventually have no idea about their risks, they have lots of CSRF all over the place
That is a separate problem, and you don't solve that by storing your session IDs differently and trying to sidestep it. You store it by actually mitigating the problem you are having, which means adding CSRF tokens.
That's the only actually secure solution to this problem - "manually handling sessions" and "secure session storage" are mutually exclusive, and automatically handling sessions will always be vulnerable to CSRF problems.
> or using a too small ID or eventually the ID is not really random.
Use a well-tested implementation - as, again, I have stated in my article - and this will not be a problem. Don't roll your own session implementation.
JWT is as secure as any other solution and this article proves it since he isn't well informed, he just throws a bunch of stuff out and tries to be "an expert". Btw. every other solution is really dangerous as well, especially Cookies could be a total mess of security problems. The same could actually happen with Bearer Tokens.
My toughts are use the thing your most happy with and the thing you understand and the thing you can implement with code that is totally clear and especially code that is really really easy to understand. And actually JWT will mostly win this battle.
I've seen a lot of people just using Session Cookies from their Framework, but eventually have no idea about their risks, they have lots of CSRF all over the place, or using a too small ID or eventually the ID is not really random. Eventually this could happen with JWT too, tough. As seen by this guy, that he generates the JWT without any well known secure string. that could be changed.