-
-
Notifications
You must be signed in to change notification settings - Fork 752
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
You should be able to revoke JWT's #1336
Comments
I did a bunch more reading this weekend about this and I think that keeping TTL's for our JWT access tokens short (ie. < 30 minutes) is better than managing a blacklist. The problem with maintaining a blacklist of of revoked tokens is that you effectively need to have a centralized store for the blacklist (ie. most likely a redis cache or your db that all services or at least the auth service have access to), which is pretty much a session store and imho kind of removes the statelessness of JWT. Not to mention it's added complexity for new projects. However, we do need a solution, in the event that a JWT is stolen. In reality, the likelihood of this is low if you are using HTTPS but it could happen if you are susceptible to XSS. Proposed SolutionI think this is how stuff should work:
All this happens over HTTPS. If a request with an invalid token is made the client is directed to re-authenticate (ie. login again). This effectively creates a rolling window, whereby the client is only authenticated for as long as they are actively making requests within the TTL of the JWT. If you are concerned with a security breach within this window you can simply change the How to refresh tokens fit in?So I think our current setup is working pretty well (except that the 1 day TTL is too large) and with these changes will work just fine for web. Refresh tokens aren't that valid for web because they can't really be stored securely (I could be wrong here) but they are a good solution for mobile. It would be a shitty user experience to have to log in every time you open the app because you had it backgrounded for > 30 minutes. Since refresh tokens can be stored securely on the device I'm suggesting that we do support this or at least provide a guide for how to do it. How this might work
When refreshing the access token using the refresh token the server would look up the refresh token, ensure it exists and is not expired. If it is invalid return an error to re-authenticate. If not, return a new access token. Some notes:
Changes Required
Additional Questions/Concerns/Thoughts
Resources
Would love to hear thoughts @BigAB @feathersjs/core-team |
I'd like to support something like
I don't think that scopes should be stored in the token. If we want to avoid hitting the database we can cache the users by id in memory (or another cache where we could again use our key-value store interface) and listen to Everything else sounds good to me. |
Sorry for the late reply to this. Here are my thoughts. The rolling windowThe "rolling window" part of this idea strikes me as a problem. It's this idea of the "lottery" request, where if you happen to make your next request between 29:00 and 29:59 you'll be refreshed, but otherwise you'll be logged out, honestly in practice this may as well just log out users every 30 minutes, because it will feel like that to them anyway for how many times this will happen. The configuration doesn't matter, it's the base concept that seems flawed. To keep it stateless, and keep an active user logged in, you may as well issue a new token with every single valid request (you could even debounce for 1 min maybe). I'd be willing to bet that that would be "performant enough" and keep the basic outline of what you want. You still have the same weakness though: attacker who gets JWT can eternally refresh by hitting the API every 29 mins, just to keep it valid. The refresh-tokenSo basically, as I understand it, the point of the refresh-token is to extend the effective TTL of the access-token, by allowing request for an new access-token to succeed without credentials for the lifetime of the refresh-token. (so like for example your mobile app example, where mobile apps stay logged in for a long time, or until revoked/blacklisted).
This part you've written about refresh-tokens seems a little weird to me. Presumably you wouldn't "revoke" your refresh-token from the client that is storing the token, that would be exactly like "logging out" (which should delete both access and refresh-tokens). The need for revoking or black listing would be from the server, were you think for whatever reason a token was compromised, or you are preventing from being compromised in the future. I wouldn't confuse this with a client choosing to log out though. Actually, I start to question the value of the refresh-token if it cant be revoked, doesn't it end up being just like having a longer lived access-token at that point? (maybe it's quicker to read out of the insecure storage, so there is a slight benefit for client side performance, but would that be worth it?) I don't know. In shortI really dislike the idea of the "window of opportunity" to refresh automatically, it sounds like it would never work. Maybe a new token every request would work, but what would that do to performance. Is the refresh token worth it if it can't be revoked? Could you just give non-web clients longer lived JWTs and store them securely? |
Any updates on the "state of the art" way to do this? |
Create a service for removed tokens and create a JWT verifier that checks the token against that service. The existing verifier is a good base, all you should have to do is additionally do a |
Isn't local-storage more vulnerable than a cookie? |
@jacktuck Not really, they just aren't vulnerable to the same type of attack:
If your webapp uses either of those, it needs to be protected against the relevant attack vector. |
@amaurymartiny Thanks On another note, has anyone implemented revocation without storing the tokens themselves. I'm personally not a fan of storing long lived tokens (access token or refresh token) on the server. It's kind of like storing passwords isn't it? How about storing an integer on the user which corresponds to an integer stored in the JWT (like a jwt version). So when you validate the JWT you check the claim for this counter is equal to what you have stored on the user. Then whenever you want to revoke a user's tokens you increment the counter on the user profile so they no longer match. For example when user changes their password, logs out of all devices or user is misbehaving. Pros:
Cons:
Would be interested on thoughts on this approach, are there any cons/caveats i have missed? :) |
Here's another "state of the art" approach with more flexibility for revokation: |
This is a good discussion. There is nothing in the JWT specification, the RFC 7519, that describe the revocation mechanism. It's relatively easy to implement such revocation mechanism, and this implies in loosing the statelessness aspect of JWT, as the server will have to maintain a list of the revoked JWTs. A common approach for revoking JWTs is to store the It worth mentioning: using this mechanism invalidates the benefits of JWT. You'll have to check the JWT signature and also the database in order to see if the JWT is revoked. If the revoking mechanism is needed, it could be simplified by using a simple string token. |
By default in Feathers, and in general any framework really, the user object is already fetched from the datastore on each request. This is done because most often the authorization logic requires looking at the user object (e.g. organisationId, role, etc.). Checking some extra column for expiration details (either to disallow invalidated token or to force user to relogin every 30 days) comes at no extra cost in that case. This is just a default behaviour of course and you could avoid querying the user on each request if you have a high load api. |
Documentation for this has been added to the Cookbook. |
At some point we need to implement this I think. It's not a massive rush though as this can be left to the developer. It's also not super trivial but if you are using HTTPS, because we don't expose the JWT in the query string, it's not very likely that it would be maliciously stolen.
See https://auth0.com/blog/2015/03/10/blacklist-json-web-token-api-keys/ for details.
The text was updated successfully, but these errors were encountered: