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.
So you can't invalidate a single user's session without invalidating all users' sessions.