7 ms·
For people using JWT as a substitute for stateful sessions, how do you handle renewal (or revocation)? With a traditional session, the token is set to expire a
by StevePerkins 10y ago
For people using JWT as a substitute for stateful sessions, how do you handle renewal (or revocation)?
With a traditional session, the token is set to expire after some period of inactivity (e.g. one hour). Subsequent requests push out the expiration... so that it's always one hour from the last activity, rather than one hour from initial authentication.
With JWT, the expiration time is baked into the token and seems effectively immutable. To extend the session, you have to either:
1. Re-authenticate from the browser every hour and store a new JWT token, which is kind of an awful user experience, or
2. Renew the JWT token from the server side every hour. At least in Google's implementation, this requires the user to grant "offline access" (another awful user experience)... and you'd need some hacky approach for replacing the JWT token in the user's browser.
So with all the recent discussion about not using JWT for sessions, what do you guys do? Do you simply make users re-authenticate every hour? Is there another magic trick that no one has brought up?
In my own shop, we're using the browser's JWT token as a server-side cache key... and storing the "real" expiration in cache so that it can be extended (or revoked) as needed. I would be interested to know if others take a similar approach, or have issues with that?
- darksaints 10y agoWith JWT, you have the option of stateful or stateless. Stateless gives you cheap federation (any server can authenticate a token issued by another server), but you lose the ability to handle revocation without some sort of statefulness introduced (a redis cache with revoked token ids for example). Stateful is basically a non-cookie based session. One possible alternative to enable auto-renewal is to issue a new token with every request, and manually bake in the persistence of the token into your front end client. In my own system, I have login with facebook, which submits current FB auth tokens with every request, after which I issue my own app token with all the necessary authorization information for my business logic. Whenever the app token expires, I attempt re-authentication using facebook login, and if successful I send back an updated app token. The front end client has logic built in to compare and swap app tokens if they change and persist in sessionStorage. It's pretty hacky. I'm a little worried about vulnerabilities that I might be introducing. I luck out in that I don't have a public API which would force the client to implement my front end logic. But it works, for now.
- tptacek 10y ago"A Redis cache with revoked token IDs". Default-allow revocation. What's not to love about JWT?
- jessaustin 10y agoI don't see how else this could work. If we require re-auth e.g. every 10 minutes, then make 10 minutes the token lifetime. If there is one source of "authentication truth" that must be consulted every time, then we don't want tokens anyway, because we have to wait on the truth for every single request.
- moyok 10y agoFor this, you can use refresh tokens and set the JWT expiration to a low interval - say 10 minutes. After every 10 minutes, the JWT expires,authentication fails, and the client uses the refresh token to get a new JWT. To revoke a client, revoke their refresh token. This way, though they won't be logged out immediately, they would be logged out in a max of 10 minutes when they need to refresh again, and find out that their refresh token is no longer valid. The point is that instead of every request touching your DB or cache, only one request does in every 10 minutes.
- zappo2938 10y agoI've been working on rolling my own authentication layer in Node/Express lately. Your comment is the first time I understood how refresh tokens work.
- hharnisch 10y agoI've captured this exact workflow, and it works really well in practice on mobile and browser (probably anything that can curl with JSON) https://github.com/hharnisc/auth-service https://github.com/hharnisc/auth-service
- zackmorris 10y agoI know this works, and I've used it, but I also find it to be the most aggravating thing about JWT and also OAuth. With OAuth, some sites allow you to refresh a token after it times out (so really the refresh token is the source of truth, defeating the purpose of the OAuth token), and others only allow you to refresh before it times out (forcing a login by the user if they are disconnected too long, or storing their username/password in the system keychain, making that the source of truth and again defeating the purpose of the OAuth token). Also a timeout of this kind is only security theater, because it may only take moments to vacuum a client's data once a token has been skimmed. The JWT library I'm using in Laravel blacklists tokens by storing them in their own table, rather than invalidating them. This is self-evidently a pretty bad vulnerability because malicious users could fill up the database, so now the developer has to deal with that scenario. Put all that together and I think the notion of token expiration is, well, dumb. In fact I think timeouts of any kind are a code smell. They make otherwise deterministic code very difficult to reason about in the edge cases. The best thing to do is encapsulate timeout handling in a refresh layer of some kind, once again defeating the whole purpose of timeouts in the first place.
- grosbisou 10y agoIn my system the authentication strategy is the responsabilities of the clients. The auth system only provides tokens via the /auth and /refresh_token routes given respectively a usn/pwd or a valid token. So the client can refresh the token when they are close to expire or just auth again after expiration.
- paulftw 10y agoThink how you would do that with session cookies - you either issue a fresh cookie or ask for login/password. Same with JWT - your client-server API wrapper can add a renewed token to a response when needed. There are many other reasons to have central API wrapper on the client (common error checking, serialisation, headers etc) so it doesn't cost much to stick an extra check for a new token
- skylan_q 10y agoI found this: http://www.cse.msu.edu/~alexliu/publications/Cookie/cookie.pdf http://www.cse.msu.edu/~alexliu/publications/Cookie/cookie.p... here: http://security.stackexchange.com/questions/7398/secure-session-cookies http://security.stackexchange.com/questions/7398/secure-sess... And implemented it. It was quick, cheap, and easy enough for me to assign a new token with most (if not every) request that required authorization/authentication. The only bits of info kept in the cookie were insensitive bits of data, so if a single token got cracked it wouldn't be a huge deal.
- iheartmemcache 10y agoRe-issuing tokens sounds like a poor-man's nonce. I skimmed the pseudo-code in their paper, immediately said to myself 'uhh..', then a section later they addressed my 'uhh..' by admitting replay attacks are effectively trivial. In some cases, no security is better than bad security, because at least your users are aware of the insecurity. (Granted, you're protecting against the replay attack - my point still stands for anyone even considering implementing something based on that paper.)
- nawitus 10y ago> For people using JWT as a substitute for stateful sessions, how do you handle renewal (or revocation)? You can do the inverse, e.g. instead of storing 'active sessions', you just store 'revoked sessions'.
- Alex3917 10y ago> storing the "real" expiration in cache so that it can be extended (or revoked) as needed. You can also have a password_last_changed field on your user model, where any token issued before this date is considered invalid. That was if a user's account somehow gets compromised, all they need to do is change their password and then all of their existing sessions are expired automatically. I can't think of any good reason for storing the expiration dates of each individual token, although maybe there is a use case somewhere.
- jcoffland 10y agoYour missing the whole point. If the server was to track password_last_changed it might as well just track user_currently_loggedin.
- blazespin 10y agoyou can do a combination of session and jwt. your firewall manages the session, and the jwt can be used internally and effective decouples your architecture and the firewall (except you will need to send a logout message to the firewall if the user choses to logout) the jwt is useful as a capability model if you've broken your internal arch into stateless microservices.
- jcoffland 10y ago"1. Re-authenticate from the browser every hour and store a new JWT token, which is kind of an awful user experience" The old token can be used to request a new token with an extended expiration, before it timesout. This can easily happen behind the scenes, so it does not affect the user experience at all. The real problem is that you cannot enforce logouts. If you just accept that you cannot 100% enforce logouts and then it works fine. A logout is performed by the client side code deleting the token. There is no way of knowing that all copies of the token were really deleted. It is imperative that HTTPS is used so that the token cannot be easily stolen. You could also bind the token to a specific IP but this would fail for devices with dynamic IPs that could change at any time.
- StevePerkins 10y agoRight. You can't logout, and you can't extend the expiration time without the user granting an "offline access" scope during the login process. So WHY exactly is this better than a simple distributed session store (e.g. Redis or whatever), with a browser header or signed cookie as the cache key? That's the underlying premise of all this recent discussion that eludes me. It seems an argument could certainly be made that you don't need to use sessions if you don't need sessions. But you kinda DO need sessions if you do need sessions. I think all of these recent blogs hand-wave over that, and much of the recent "JWT instead of sessions" chatter is people just repeating a mantra that they read in blog posts because it seems like the hot new mantra.
- stephenhuey 10y agoAnother benefit is providing a standard way to access multiple services at once with a single token. It is possible to force a logout on the server side if a problem arises, and you wouldn't need to store these invalidated tokens in the database because it'd likely be a small blacklist you make available to all the servers (and a blacklisted token would be in that list only until the expiration time).
- StevePerkins 10y ago
- Bombthecat 10y agoAs someone working with it, we use it only for machine to machine communication. There it is easy to reauth. I don't think jwt was ever ment to used in browsers..
- bigonlogn 10y agoJWT is just a token. It's not some panacea of client-side only authentication. There are a lot of people lamenting the difficulty in performing logout via JWT. I believe people are missing the point. The failing isn't with JWT, it's with the implementation of the session system. Typically with sessions the client has a session key. The key gets sent to the server where it looks up the session (via. memory, cache, database, whatever). You can create a new session, validate an existing session, or end a session. All using that key. They only difference between JWT and cookies is JWTs aren't automatically sent with every request. You have to explicitly send them. I believe this is a good thing. It avoids some common attack vectors.
- lllorddino 10y agoIs there anything wrong with saving the token in the cookie? I'm not exactly sure how to save them in the header. I'm guessing save it to localStorage and use javascript to pass it back to the server?
- jastanton 10y agoThis article talked a bit about putting them in cookies: The header method is preferred for security reasons - cookies would be susceptible to CSRF (Cross Site Request Forgery) unless CSRF tokens were used. Secondly, the cookies can be sent back only to the same domain (or at most second level domain) they were issued from. If the authentication service resides on a different domain, cookies require much more wild creativeness. As far as putting something in the header, if you're using javascript check out superagent. It's as easy as: request(url).set('SomeHeader', 'SomeValue'); or the latest http fetch api just do: var request = new Request('/users.json', {method: 'POST', headers: new Headers({'Content-Type': 'text/plain'}) }); fetch(request).then(function() { /* handle response */ });
- bigonlogn 10y agoYou can also supply an options object (including headers) as the second argument[1]. [1]https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch...