-
Notifications
You must be signed in to change notification settings - Fork 673
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
Sliding Token Clarification #154
Comments
@IvanFon were you able to solve this? |
No, unfortunately. I ended up using separate auth and refresh tokens, which works fine.
It’s not a huge hassle to use two separate tokens, but in my case it would be slightly more convenient to use a single sliding token. Having separate tokens wouldn’t improve my security, both tokens are stored in memory/localStorage. If an attacker gets access to one, they have access to both.
…On October 17, 2019 6:06:04 a.m. EDT, BlackXnt ***@***.***> wrote:
@IvanFon were you able to solve this?
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#154 (comment)
|
I think I'm having the same problem... So I dug into the source a little to see if I could see what's going on. TL;DRA low-level verification function is checking if What Goes WrongHere's a nice, long stack-trace getting to the bottom of what's happening... We'll start at the TokenRefreshSlidingSerializer, called with the refresh api, which looks like this: class TokenRefreshSlidingSerializer(serializers.Serializer):
token = serializers.CharField()
def validate(self, attrs):
token = SlidingToken(attrs['token'])
# Check that the timestamp in the "refresh_exp" claim has not
# passed
token.check_exp(api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM)
# Update the "exp" claim
token.set_exp()
return {'token': str(token)} The token gets rejected at the class SlidingToken(BlacklistMixin, Token):
token_type = 'sliding'
lifetime = api_settings.SLIDING_TOKEN_LIFETIME
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.token is None:
# Set sliding refresh expiration claim if new token
self.set_exp(
api_settings.SLIDING_TOKEN_REFRESH_EXP_CLAIM,
from_time=self.current_time,
lifetime=api_settings.SLIDING_TOKEN_REFRESH_LIFETIME,
) Killed at the def __init__(self, token=None, verify=True):
"""
!!!! IMPORTANT !!!! MUST raise a TokenError with a user-facing error
message if the given token is invalid, expired, or otherwise not safe
to use.
"""
if self.token_type is None or self.lifetime is None:
raise TokenError(_('Cannot create token with no type or lifetime'))
self.token = token
self.current_time = aware_utcnow()
# Set up token
if token is not None:
# An encoded token was provided
from .state import token_backend
# Decode token
try:
self.payload = token_backend.decode(token, verify=verify)
except TokenBackendError:
raise TokenError(_('Token is invalid or expired'))
if verify:
self.verify()
else:
# New token. Skip all the verification steps.
self.payload = {api_settings.TOKEN_TYPE_CLAIM: self.token_type}
# Set "exp" claim with default value
self.set_exp(from_time=self.current_time, lifetime=self.lifetime)
# Set "jti" claim
self.set_jti()
def decode(self, token, verify=True):
"""
Performs a validation of the given token and returns its payload
dictionary.
Raises a `TokenBackendError` if the token is malformed, if its
signature check fails, or if its 'exp' claim indicates it has expired.
"""
try:
return jwt.decode(token, self.verifying_key, algorithms=[self.algorithm], verify=verify)
except InvalidTokenError:
raise TokenBackendError(_('Token is invalid or expired')) Yup that's right, it's only checking the exp key, not the refresh_exp key. Just to be thorough, let's see what's going on in the jwt library. A Brute-Force SolutionTo fix this, one could change the SlidingToken initialization in the token = SlidingToken(attrs['token']) to token = SlidingToken(attrs['token'], verify=False) which I successfully tested, but I don't know the library well enough to understand the ramifications of that kind of action. I hope this helps someone, somewhere? |
Yeah, I included the whole sliding token thing to provide some kind of backwards compatibility with users of the legacy |
An alternative, as suggested @IvanFon is to use pair token. Dont forget to set 'ROTATE_REFRESH_TOKENS' to True, if you decide to go for the pair token approach. I forgot this flag existed which made me believe sliding tokens was the only alternative. |
Just wanted to add that I've also stumbled upon this issue. Thanks @decepulis for digging into the issue and finding a workaround in the meantime. |
I bumped in to this issue aswell since I wanted a fault proof system where only one valid token was present for a user. When using a pair, the access token is still valid for a certain time even though the refresh has been blacklisted. One additional step to add is that when overriding the serializer, you should also add |
Have a security error in this solution. With |
one doubt, how do i create a sliding Token?? |
Please don't use sliding tokens. They're confusing. (They came from migrating from drf-jwt). We may be deprecating is soon as migration from drf-jwt is much lower than before. |
ok thanks @Andrew-Chen-Wang |
Sorry to resurect this thread @Andrew-Chen-Wang but I have stumbled on this myself and it bugs us. Our use case is that we want to make sure that only one device is used at a time, and We could not achieve that with Access/refresh tokens + blacklist as only refresh tokens get blasklisted, so even if I blacklist all user tokens before login , I still have this access token that has 1 minute lifetime and a user can use the app on two devices, which we don't want. We can achieve that with sliding tokens but then after the expire time refresh doesnt work so user get's returned to login, which is not the desired functionality... Is it possible to do my use case with access/refresh tokens ? If not, is it a lot of effort to fix the issue that @decepulis described with changing the |
No problem. I am not going to be deprecating sliding tokens as I understand some people do like them, especially if they're coming from drf-jwt. Also, school has bogged me down, so I've been maintaining repos via PRs lately anyways.
Sure. Like how we throttle users within the same IP address but we don't know the private IPs, we store several features of a device to make as unique of an identifier as possible. You can do two options (assuming not a malicious user obviously and this is just a regular ol joe):
Hope that helps! |
Thanks for your reply @Andrew-Chen-Wang but unfortunately we cannot store any information about the users. If you don't have time maybe I can help with the issue? If i understand correctly, the decode function for sliding tokens is the same as for regular tokens but it should be a little different (incorporate |
If you're trying to use access/refresh, people above have resolved this using a setting for returning new token pairs.
I wouldn't mind a PR for the fix for sliding tokens. Just won't be online for the next few weeks. |
@Andrew-Chen-Wang Unfortunately this still leaves the time window when the access token can be used, as blacklist only affects refresh tokens (just tested it).
I'll keep you posted whether I have time to help with this, as I'm working on other stuff right now. |
@DBlek Have you solved the problem? I've got stuck with the same problem. |
We have gone with a custom feature that blacklists all user tokens before giving a new one |
You mean you blacklist all previous tokens of that specific user, or all tokens of all users? |
only for the user |
So assume user has logged in before, and tries to login again with another device, while previous token is valid. |
We used OutstandingToken, filter out the ones with the user ID and add them to BlacklistedToken. Both can be stored in the DB. Periodically we delete all blacklisted and outstanding tokens So to be crystal clear: |
@DBlek Brilliant! I could solve it, with your amazing help. But there is still this problem of not being able to limit users to use one token on multiple devices. If you find any solution, please let us know. And BTW, what about this misconfiguration of sliding tokens refresh lifetime? Is there any stable solution to fix it? |
@famdude great u got it working - just saw ur msg :) All should be in docs for my solution so nothing new ;) |
Hi, I'm trying to use sliding tokens, but I think I need a bit of clarification. From what I understand, when I generate a sliding token, it can be used for authentication until it's expiration claim expires. After the expiration claim expires, I can still refresh the token until the refresh expiration claim expires.
The problem I'm running into is that I can only refresh the token while the auth expiration claim is valid. I'm not sure if this is intended or if I'm doing something wrong, but this makes it seem like there's no point to the refresh expiration claim.
My config is:
I can obtain a token:
I can use that token for authentication:
After
SLIDING_TOKEN_LIFETIME
(1 minute), trying to make authenticated requests gives a 401, which I would expect:But when I try to refresh the token within
SLIDING_TOKEN_REFRESH_LIFETIME
:But if I try refreshing a token within
SLIDING_TOKEN_LIFETIME
, it works fine:So am I doing something wrong? What is
SLIDING_TOKEN_REFRESH_LIFETIME
for? The way I expected to use a sliding token would be to retrieve a token on login, and use it for requests. Once a request returns a 401 telling me the token expired, I refresh it to obtain a new token, and repeat the process.Thanks! Aside from refreshing sliding tokens, this library has been super easy to use :)
The text was updated successfully, but these errors were encountered: