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?
For 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.
I 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.
I have personally experienced the security disadvantage you mentioned. I used my Google login to sign into an email client. I immediately realised that the app was going to store all of my email data in their private servers. I quickly went over to the Google dashboard and deauthorised the app, relieved that they would only have been able to get my first few mails in this time. But the app retained access to my emails, even receiving new emails for some time. Probably because of something similar to a refresh token being revoked, but the access token still being valid. I wanted to stop the app from accessing my e-mails, but could not.
However, despite this disadvantage some applications just cannot afford the load of every single request touching the DB or cache. JWT makes sense for that particular use case when you are willing to make this compromise. Instead of every single request touching the cache, maybe every 1000th request does now, because of the token expiration time.
Another use case is when you need a very simple, stateless way to authenticate users and don't require revocation. Some Oauth providers don't give you the option to revoke access tokens, for example.
> despite this disadvantage some applications just cannot afford the load of every single request touching the DB or cache.
Disagree. This is one of the simplest things alive to distribute. Split the query from the token do the query in the DB and the token lookup is effectively a distributed hash table lookup (assuming the token is, say, a UUID). Once the DB query comes back store the result pending the successful retrieval of the token.
What's difficult is handling something like millions of concurrent video downloads / uploads - not looking up tiny tokens.
Sure, but the really hot tokens could be cached right next to the DB. Plus how many operations is a person doing per second? If its more than a couple you can batch them pretty easily.
Think of it this way, the _real_ token is your refresh token. It's stored in your database. You control it and it can be revoked at any time. So, now you build 100 other services, and they all accept this refresh token. Problem is, since you control it so well, every other service now needs to validate that refresh token with the auth service on every request. It would be really nice if we could get around that massive traffic pinch point. So, we create crypto tokens that can be validated by every service without the need to make a network call. As a compromise, we make this new token expire in an hour so that the _client_ needs to validate their refresh token every hour and all our services are freed of from ever directly calling the auth service. Sure, this means that when you log out you're not really logged out for up to an hour, but it's all tradeoffs.
For many applications, not being able to immediately log out is an unacceptable trade-off. If you know your account has been compromised and you need to kill all sessions ASAP, an hour delay is unacceptable.
> The JWT library I'm using in Laravel blacklists tokens by storing them in their own table, rather than invalidating them.
The main rationale for JWTs is that it removes the session store as a point of contention (and secondarily it resolves some xdomain issues that aren't that difficult to work around anyway). If you're going to introduce a new table/cache, you're likely better off just using sessions.
I'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
Ehh... but at least for Google's implementation, you won't have a refresh token in the first place if the user doesn't grant "offline access" on a special page that comes up after login.
In our usability testing, we've found that this freaks a lot of people out and reduces adoption. The benefits of basically outsourcing our session management to Google don't outweigh this... so we use JWT for auth only, and then use that token as a session key for our own local solution.
> the client uses the refresh token to get a new JWT
What's the point of using the JWT then? If the refresh token lasts for longer than the JWT, and you need to send it periodically back up to the server to auth the user, why use the JWT in the first place?
The point is that it would reduce the DB/cache load, as the refresh token would need to be verified once in a few minutes or so as opposed to verifying it for every request. Regular requests could be authenticated in the CPU itself without having to go through to the DB/cache layer. This means lower latency and reduced load on the DB/cache.
If the JWT is as long as the refresh token, then what's the point of having a refresh token? You would then probably need to get new refresh tokens then to make the session last longer.
The idea is to make the refresh token last for say a few days, and the JWT for say 10 minutes. Now, every 10 minutes the client needs to use the refresh token to get a new JWT. The maximum time a client can have access to the service without a valid refresh token is 10 minutes. All the requests made in this window of 10 minutes would be deemed authenticated by verifying the JWT, and without having to go through the database or cache.
Now, say a user of a web app clicks "log me out from all my devices". The user's access needs to be revoked from everywhere they are logged in. If you invalidate all their refresh tokens, then in a max of 10 minutes they would be logged out from everywhere, as their refresh tokens would no longer work and the JWT duration is only 10 minutes.
This approach is essentially a mid-way or a tradeoff between using traditional sessions and JWT. "Pure" JWT is stateless and hence cannot support individual session revocation.
The only way to invalidate sessions in "pure" JWT would be to invalidate the key or certificate used to sign the JWT, but that would invalidate everyone else's sessions as well and hence is not very practical.
Since with this approach you implement sessions plus JWT, it's more complicated than just using sessions. JWT should be used for such applications when the latency or load benefit is significant enough to justify the added complexity.
For applications that do not need session revocation, however, JWTs are a convenient way to implement sessions without needing a DB or cache layer.
It's so that if the JWT is stolen in transit, the thief only has access to the token for a shorter period of time. This is why they should expire quickly. Whether or not you think that matters, is not up for debate. That's how it is.
JWT 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.
Is 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?
This 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 */ });
"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.
Right. 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.
I think your issue is that you're trying to wrap Google's auth in your own jwt implementation. This setup can only prove your own trust of the tokens you create.
You can have a user auth with google and provide you a user id, you can then slap that id into a token and know that you gave that token to the right user and the info inside that token is what you put there. It does NOT let you keep other sessions alive on its own. You won't be able to get around Google's expiry this way if you need a valid auth to Google's APIs.
What it does do is let you auth a user through google once at the beginning of your own app's session, and not have to keep track of session in your own database or hit google multiple times in a user session.
Another 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).
> access multiple services at once with a single token
If you're talking about multiple microservices under your own control, then they could all work with your distributed session store just as easily (if not more so).
If you're talking about SSO across multiple third-party services, then perhaps JWT could be a nicer solution than SAML. However, here you're talking about a single-digit percentage of edge cases. Not enough to declare "JWT over sessions" as a general rule.
With 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.
I 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.
> 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.
Think 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
In 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.
you 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.
Re-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.)
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?