Closed AGaliciaMartinez closed 1 year ago
Hi @AGaliciaMartinez , Kindly check the token you use whether the issued_at time is greater than current (adding leeway as well). This is the case which was handled as part of this Pull Request .
I request you take a look at the PR description to understand it better why we need to include this check and also check your code/token what's the current_time and issued_at time is to ensure the same.
Different servers could have different time settings, especially when they are not connected to the internet, like a private network. Not every private network has a clock synchronization mechanism.
In that cases we can use the UTC time as a standard time. Also for the time difference in clock synchronization we can use the leeway time to make it work.
@auvipy any added inputs will be helpful here
I've also encountered this issue, particularly in tests where the token is issued within a few milliseconds of when it gets used. The problem is that the iat
is assumed to be an integer, while the RFC allows floats. If a token is issued with a float iat
and decoded within that second, the code will test against the integer second, which is less than the float iat
. This results in ImmatureSignatureError
being raised.
Here is an example that used to succeed, but now raises an error:
jwt.decode(jwt.encode({'iat': time.time()}, 'secret', algorithm='HS256'), 'secret', algorithms=['HS256'])
I suggest either allowing full float calculations or casting the input iat
to an int.
In that cases we can use the UTC time as a standard time. Also for the time difference in clock synchronization we can use the leeway time to make it work.
@auvipy any added inputs will be helpful here
we need to get rid of the type error
It's yes and no, since most JWT libraries in other languages also support the int
type this could cause an issue. I agree with the fact that float has a bit more precision than int but to tackle this synchronization issue the recommended way is leeway time
. So adding the minimum leeway time will help them without changing the custom logic they have now.
An example could be,
jwt.decode(jwt.encode({'iat': int(time.time())+leeway}, 'secret', algorithm='HS256'), 'secret', algorithms=['HS256'])
With this, PyJWT
will be in sync with other frameworks/libraries in other languages as well.
We've also been hit by this bug, broke all our tests. Our 'iat' stores the current time as a float.
Hi,
The check of iat
as an integer exisits for a long time. The new change that went in is the ImmatureSignatureError
when the iat
is greater than current time + leeway
.
@auvipy I hope this is something we all need to take a call on.
One simple "fix" would be to cast iat
to an int, for example:
iat = payload["iat"]
try:
iat = int(iat)
except ValueError:
raise InvalidIssuedAtError("Issued At claim (iat) must be an integer.")
This would allow PyJWT
to only deal with integers, but be compatible with sources that use floats.
It is possible to use leeway
instead, but if that is the approach to be taken, maybe the default should be leeway=1
instead of leeway=0
so existing code continues to work.
what if instead of: https://github.com/jpadilla/pyjwt/blob/0cfc0978a6c9dc01d89043d203b7f397e78822c2/jwt/api_jwt.py#L190
we just did
now = datetime.now(tz=timezone.utc).timestamp()
Wouldn't that mostly address the issues seen? Anything else would be addressed by specifying a leeway when decoding. unsure if i'm missing anything else.
@jpadilla your solution should work,
I also got a few tests failing when upgrading pyjwt :
/usr/local/lib/python3.9/site-packages/jwt/api_jwt.py:193: in _validate_claims
self._validate_iat(payload, now, leeway)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <jwt.api_jwt.PyJWT object at 0xffff9257b910>
payload = {'access': True, 'event_id': 10, 'exp': 1667564373.7657802, 'iat': 1667564058.7661693, ...}, now = 1667564058
leeway = 0
def _validate_iat(self, payload, now, leeway):
iat = payload["iat"]
try:
int(iat)
except ValueError:
raise InvalidIssuedAtError("Issued At claim (iat) must be an integer.")
if iat > (now + leeway):
> raise ImmatureSignatureError("The token is not yet valid (iat)")
E jwt.exceptions.ImmatureSignatureError: The token is not yet valid (iat)
/usr/local/lib/python3.9/site-packages/jwt/api_jwt.py:219: ImmatureSignatureError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB post_mortem (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /usr/local/lib/python3.9/site-packages/jwt/api_jwt.py(219)_validate_iat()
-> raise ImmatureSignatureError("The token is not yet valid (iat)")
(Pdb) iat
1667564058.7661693
(Pdb) now
1667564058
(Pdb)
the value of «now» seems not to include milliseconds, because
per timegm source code, it’s only seconds :
def timegm(tuple):
"""Unrelated but handy function to calculate Unix timestamp from GMT."""
year, month, day, hour, minute, second = tuple[:6]
days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1
hours = days*24 + hour
minutes = hours*60 + minute
seconds = minutes*60 + second
return seconds
we could bypass this with a leeway of 1 second, but that’s a lot of leeway
It's yes and no, since most JWT libraries in other languages also support the
int
type this could cause an issue. I agree with the fact that float has a bit more precision than int but to tackle this synchronization issue the recommended way isleeway time
. So adding the minimum leeway time will help them without changing the custom logic they have now.An example could be,
jwt.decode(jwt.encode({'iat': int(time.time())+leeway}, 'secret', algorithm='HS256'), 'secret', algorithms=['HS256'])
With this,
PyJWT
will be in sync with other frameworks/libraries in other languages as well.
i don't think it's a good solutions.
Hi. Had the same issue, when updating from 2.4.0 to 2.6.0... drove me crazy this bug.
Solved it by simply deactivate the check with: options={"verify_iat":False} in decode:
self.decoded_token = jwt.decode( token_str, ActivationToken.pub_key, algorithms=["RS256"], options={"verify_iat":False} )
But I have to admit, I have wrote a custom check for the iat already before that update with an higher tolerance value (I think this is what you have labeled as leeway).
will there be any tagged release with this fix?
I've also encountered this issue, particularly in tests where the token is issued within a few milliseconds of when it gets used. The problem is that the
iat
is assumed to be an integer, while the RFC allows floats. If a token is issued with a floatiat
and decoded within that second, the code will test against the integer second, which is less than the floatiat
. This results inImmatureSignatureError
being raised.Here is an example that used to succeed, but now raises an error:
jwt.decode(jwt.encode({'iat': time.time()}, 'secret', algorithm='HS256'), 'secret', algorithms=['HS256'])
I suggest either allowing full float calculations or casting the input
iat
to an int.
I took this advice and casted the payload I'm passing to 'iat' to int like this:
def create_jwt(cls, secret_claims):
key = cls.get_key()
pay_f = Fernet(key.get('payload_key'))
gmt = tz.gettz('GMT')
now = datetime.datetime.now(tz=gmt)
exp = now + datetime.timedelta(days=1)
payload = {
'iat': int(now.timestamp()), # casted to int
'exp': exp.timestamp(),
'nbf': now.timestamp(),
'iss': env('PROJECT_NAME', 'pfunk'),
'til': pay_f.encrypt(json.dumps(secret_claims).encode()).decode()
}
return jwt.encode(payload, key.get('signature_key'), algorithm="HS256", headers={'kid': key.get('kid')}), exp
It worked for me. Thank you! (Although I will still watch this thread)
I've also encountered this issue, particularly in tests where the token is issued within a few milliseconds of when it gets used. The problem is that the
iat
is assumed to be an integer, while the RFC allows floats. If a token is issued with a floatiat
and decoded within that second, the code will test against the integer second, which is less than the floatiat
. This results inImmatureSignatureError
being raised.Here is an example that used to succeed, but now raises an error:
jwt.decode(jwt.encode({'iat': time.time()}, 'secret', algorithm='HS256'), 'secret', algorithms=['HS256'])
I suggest either allowing full float calculations or casting the input
iat
to an int.
Thanks for the great catch. But the problem is that even if now
becomes a float from query, it may still have milliseconds inaccuracy with iat
. If then it's smaller than iat
, the exception would still be thrown. I'm not sure which is the best way to solve this. Perhaps adding leeway in the client code? If that's the case then there's nothing to be modified in the library code. (I'm not sure how other JWT library handle this)
Y'all this was litigated previously at https://github.com/jpadilla/pyjwt/issues/190. The JWT spec does NOT say to reject tokens with iat
("issued at") in the future, so this behavior goes beyond the spec and is inconsistent with many other JWT libraries.
4.1.6. "iat" (Issued At) Claim
The "iat" (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
If token issuers want clients to specify that a token should not be accepted before a certain timestamp (which puts additional constraints upon clients by implying that clients' clocks should keep relatively in sync with a central clock source and/or need to check it with leeway) then the issuer is supposed to set nbf
("not before"):
4.1.5. "nbf" (Not Before) Claim
The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
I am currently getting bit by this issue a lot in a large enterprise microservices environment (where token issuer, token user, and token-accepting server which validates offline using RSA are three different machines) where clock drift is coming into play. Myself and 10+ other people have now wasted multiple days of person-time trying to figure out why tokens were being rejected, talking about how best to address it, and managing the workloads & deliverables of the people investigating & talking about this.
Could this issue be re-opened and could the proper fix be to set the default value of verify_iat
to False
and publish a 3.0 since that's a breaking change?
Clients who understand the risks and want to engage in this extra-spec behavior should opt in by setting verify_iat
to True
, and the need to do this should be announced in the changelog for this new major version. (Or maybe there could be a global variable in pyjwt
to control the verify_iat
default?)
Just for fairness, I did some due diligence on the other libraries. Every other library I've found that does this has its own respective issue complaining about it. 😉
Libraries I found that do not check that iat
is >= now:
Libraries that do (and their respective bug complaints):
Maybe this needs to be clarified in the spec since there's a pretty divided polity.......
I'm going to be submitting an errata request for RFC 7519 about this. I'll be making my draft of that & centralizing the Github-side discussion here:
Discussion: JWT tokens containing iat
values in the future should not be rejected
Lately, I've been encountering this error The token is not yet valid (iat)
(as of March 2024). Interestingly, the solution was simply to resynchronize my computer clock on Windows 11 23H2 (OS build 22631.3296)
working on WSL2.
Has anyone else encountered this issue where the clock gets out of sync, as I have experienced it multiple times this week!
My computer is relatively new, so I doubt the MB battery is causing the problem.
Lately, I've been encountering this error
The token is not yet valid (iat)
(as of March 2024). Interestingly, the solution was simply to resynchronize my computer clock onWindows 11 23H2 (OS build 22631.3296)
working on WSL2. Has anyone else encountered this issue where the clock gets out of sync, as I have experienced it multiple times this week! My computer is relatively new, so I doubt the MB battery is causing the problem.
@Samk13 I've been chasing an issue with Django Allauth + DJ Rest Auth for the last couple of hours and re-syncing my Windows clock fixed this JWT iat error being raised inside of Allauth. So first, thank you haha, but second yes it appears I am also experiencing the same thing.
See #939
We found an issue in one of our tests in the recent update of yours. Our package works with
pyjwt==2.5.0
but it breaks withpyjwt==2.6.0
. We will give more details on the issue once we explore it more and will try to provide a minimal example that reproduces the error. However, may I ask you to check if the latests changes could have broken backawards compatibility? We are particularly suspicious of #794. We think this because the error message we get:The token is not yet valid (iat)
.We are sorry we could not provide more details yet.
System Information