I am trying to add expiry time to JWE which I am generating using jwcrypto library in the following way
from jwcrypto import jwe, jwk, jwt
from datetime import datetime, timedelta
import time
# create JWK from existing key
jwk_str = '{"k":"29Js2yXM6P_4v9K1mHDlYVHw8Xvm_GEhvMTvKTRLRzY","kty":"oct"}'
jwk_key = jwk.JWK.from_json(jwk_str)
# calculate expiry time
d = datetime.now() + timedelta(seconds=5)
epoch = datetime.utcfromtimestamp(0)
total_seconds = (d - epoch).total_seconds()
# Add exp to the claims
claims={"exp": total_seconds, "sub": "Some random payload"}
print(claims)
jwttoken = jwt.JWT(header={"alg": "A256KW", "enc": "A256CBC-HS512"}, claims=claims)
jwttoken.make_encrypted_token(jwk_key)
jwetokenstr = jwttoken.serialize()
print(jwetokenstr)
# wait for 10 seconds to cross the expiry time
time.sleep(10)
jwttoken = jwt.JWT()
jwttoken.deserialize(token, jwk_key) # Ideally this line should fail as expiry is reached but it doesn't
print(jwttoken.claims)
I am getting the payload, but expiry claim is not read and doesn't fail on expiry. What I am doing wrong ?
This ends up reducing to a datetime manipulation bug.
The
exp
claim of a JSON web token should filled out with the seconds from epoch of the expiration time.datetime.now()
returns a local time (not UTC time)datetime.datetime
object. The code above then goes on to subtract this local timedatetime.datetime
object from the UTC timedatetime.datetime
object of 0-epoch time and evaluates the total seconds between these two to determine the expiry time. However, because this is comparing a local time datetime to a UTC time datetime, the number of seconds here is actually off of the epoch time by a constant factor of your local timezone difference from UTC.For example, if I live in a place where the time is 5 hours earlier than UTC, I will actually use an epoch time that is
5 * 60 * 60
seconds off of the true epoch time I want for the expiry with this code.Instead you could simply use
round(time.time()) + x
wherex
is the number of seconds forward in the future the JWT should expire.time.time()
returns the seconds from epoch (but as a float so you need to round) from epoch.For example:
gives the output