Skip to content
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

Logout Functionality #28

Open
unityliving-saravanan opened this issue Jun 14, 2018 · 22 comments
Open

Logout Functionality #28

unityliving-saravanan opened this issue Jun 14, 2018 · 22 comments

Comments

@unityliving-saravanan
Copy link

unityliving-saravanan commented Jun 14, 2018

How can i invalidate the token after the user logout form the application before token expire? Clearing the token form client side is not enough for prevent the access to protected resources. Is there any other options or work round for invalidate the key?

@davesque
Copy link
Member

Currently, there is no way to logout. I had planned to add a revocation view for tokens but I just don't have the time to do it right now.

@sergiowalls
Copy link

If refresh token were saved on database we can add a validation against it and perhaps invalidate compromised tokens.

@MihaiVisu
Copy link

In my case I instantiated a CustomAccessToken class inheriting the BlacklistMixin too and in my logout view I just blacklist the token. I know it is not very effective from a database perspective, but it was the quickest workaround in my case.

One solution might be to edit the access token and add some kind of flag in the jwt encoding highlighting whether the token has been deactivated by logging out. I am not sure if this is the most effective way.

@davesque
Copy link
Member

Actually, that's probably the best way to do it as long as you're flushing the blacklist fairly frequently. If a token is expired, it will be flushed from the blacklist. There's really no other way to log someone out with JWT's other than to have a blacklist or to change the signing keys (logging everyone out).

@marioyc
Copy link

marioyc commented Jul 23, 2018

jpadilla/django-rest-framework-jwt#385 mentions the possibility of having different signing keys per user which allows to logout only the intended user, would there be a simple way to implement this?

@davesque
Copy link
Member

davesque commented Jul 24, 2018

Hmm...yeah, I suppose that is another way. I can't think of a really straight forward way to do it right now. A possible drawback to that approach is that it could be difficult to coordinate using the same signing key across multiple services.

@wakaryry
Copy link

wakaryry commented Sep 1, 2018

You could add a judge that a token before the logout is invalid.

If add tokens into db, why not to use sessions.

@ngocngoan
Copy link

Hi!

I wrote a logout function, and I added a successful token refresh to the blacklist. But then I realized that when I send requests with access token in HTTP Header, I still get the value of success (code 200) without the unauthorized error (code 401) as expected.

@derek-adair
Copy link

Would someone mind posting a gist of this working? Seems like something beneficial to the docs.

@orehush
Copy link

orehush commented Feb 18, 2019

@derek-adair I have created gist with logout view .
@ngocngoan access token will be expired in ACCESS_TOKEN_LIFETIME and you can not refresh it, because refresh token blacklisted.

@wisnuprama wisnuprama mentioned this issue Mar 2, 2019
@volartem
Copy link

volartem commented Mar 4, 2019

Is it possible to change expires_at in current access token on logout request?

@davesque
Copy link
Member

davesque commented Mar 4, 2019

Yes, but there are still unexpired access tokens with valid signatures out in the wild at that point. So doing that wouldn't really log anyone out.

@greyhare
Copy link

The way things are supposed to work is that you have some other communication channel to JWT-using services, such as a publish-subscribe setup, that would broadcast revoked tokens. Those other services would need to add them to their blacklists.

This channel, IMHO, is outside the scope of this project, because we have no idea what it is (dbus, zeromq, smtp, who knows). The module user would be expected to add this support themselves, using the blacklist module (or equivalent) to track blacklisted tokens.

@davesque
Copy link
Member

@greyhare Yeah that seems correct. Although maybe we could add something like a signal that people could hook into if they want to build their own blacklisting channels.

@greyhare
Copy link

Right now, I guess the signal could be "blacklist object created"

@trase8
Copy link

trase8 commented Apr 1, 2019

maybe it's better to use cache or redis for blacklist storage backend? Cache may be expired for instance in 24h. So this allows not to use cron tasks

@greyhare
Copy link

greyhare commented Apr 1, 2019

I haven't used Redis much myself. Can you tell it when to expire an object? If so, then you can just set object expiration time to token expiration time.

Also, can you search for an object via its SHA-256 hash? The blacklists I've seen elsewhere just store the SHA-256 hashes of expired tokens, not the tokens themselves.

@shahriaroo
Copy link

The problem with Redis is that it's an in-memory database and therefore when it restarts it would log everyone back in (unless you use a Redis cluster or something).

@sshishov
Copy link

sshishov commented Nov 15, 2020

Could you guys please explain to me why do you store blacklisted tokens instead of whitelisted?

In the beginning, I was thinking about the saved database call on token validation, but if we are validating against blacklisted tokens, it will be the same, even worse.

If you store only VALID tokens, then it is easy to revoke and you almost no need to clean anything, except of expired refresh tokens in the database based on created_at date for instance.

In our setup we did use REDIS to store valid tokens for users as DB call is quite expensive to do on every request (as we are validating tokens against misuse and we rotate refresh token every now and then). But a lot of people just do not have REDIS setup ready and depending on the users count, you can have OOM of REDIS unexpectedly if you are not running managed service on AWS for instance.

@Andrew-Chen-Wang
Copy link
Member

Andrew-Chen-Wang commented Nov 15, 2020

@sshishov I like that idea of whitelist. There are definitely pros and cons to each, and I think the time consumption on whitelisting is much less than a full scan for the blacklist table. (even with a bunch of bots spamming, it wouldn't make a difference).

HOWEVER, I think the biggest problem with whitelisting is the data storage requirement. If we're only blacklisting a certain few tokens, data is limited. If we accept majority of users, then you've got a huge database table that needs a cron job clearing that table for expired.

Whitelisting with Redis is the same story. You don't want to fill up the data store too quickly if you're accepting the majority of incoming requests.

@sshishov
Copy link

sshishov commented Nov 16, 2020

Hello @Andrew-Chen-Wang .

If you are using database, then every access of protected API will do DB call which is not desired when we are using JWT tokens (as it is self contained).

Using REDIS, it is completely different story:

  • If we store only valid refresh tokens, we should not fill up quickly
  • Along with storing the token we can set TTL on the key (for instance tokens:<user_id>:<token_id>) the same value + 5 seconds of the token lifetime. The only issue with this approach if users will just start doing a lot of login requests where new refresh tokens will be generated every request. And we cannot easily restrict the number of tokens per user as lookup in redis with a lot of data is not VERY fast also like SCAN tokens:<user_id>:* and limit to 10, but it if we are okay with this small delay on every refresh/login, then it is way to go.
  • Another approach is to use REDIS lists on the key tokens:<user_id> with TTL of inserted token. and update TTL on every new push to the list using (LPUSH, RPOP) and here you can limit number of tokens per user, say 5. If the returned value of LPUSH more than 5, then we will do RPOP to remove the latest element from the list and update TTL of main key

It is just my assumptions and possible ways to solve the problem without degradation of the performance.


Update:

Points to consider:

  • Also using blacklist approach you will add to the database/redis every "old" refresh token after successful rotation, or not?
  • If I am user and my refresh token leaked, but then I did re-login and started using OLD token. The "bad" guy can still use old leaked token to access data. And to blacklist it, we have to know which token exactly to blacklist, how we are going to get it, only from logs?

@greyhare
Copy link

The reason you want a blacklist instead of a whitelist is because it's a much shorter list, and tokens can be removed from the blacklist when they expire. So authentication is just "is the JWT itself valid, and not in the blacklist?" Which is a hash operation plus a lookup on a small tree.

Which gets back to one of the main reasons to use JWT is that you don't need to maintain a database of valid tokens shared among the hosts trying to authenticate the token. If you're okay with keeping a database of active tokens to authenticate against, you may as well be using the non-JWT random session token model.

Some things regarding refresh tokens:

  • Maybe instead of generating a refresh token on login, you require the client to wait some time before requesting it? I.e. you can request a longer-lived refresh token, but only with a regular JWT that's at least n seconds old. Then folks who just need a one-and-done transaction don't have to deal with refresh tokens at all.
  • There should only be one refresh token for a session ever. Refresh tokens are basically session tokens, with all their problems. Maybe allow the client to optionally provide the old refresh token in the refresh request so you can expire it immediately?
  • Yes, you need to blacklist old refresh tokens. Maybe they need their own blacklist with its own rules?
  • Or maybe you only issue new refresh tokens within some time of the expiration of an old one, and set the new token to only become valid a few seconds before the old one expires?
  • "If I am user and my refresh token leaked, but then I did re-login and started using OLD token." Old which token, regular or refresh?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests