pythonpostmanpyjwt

Using a pyJWT expiration time


I'm working on a small project. I have a teacher's decorator. With the JWTs I produce on the login page, it is allowed to enter different roots. I want to generate JWTs with an expiration time. But there is a problem I can see the expiration time in Postman, but I can use it after that time has passed.Here's my decorator

def teachers():
    def wrapper(f):
        @wraps(f)
        def decorator(*args, **kwargs):
            token = request.headers["Authorization"]
            token = str.replace(str(token), 'Bearer ', '')
            try:
                data = jwt.decode(token, "testing",algorithms=["HS256"])
                print("Token is still valid and active")
            except jwt.ExpiredSignatureError:
                print("Token expired. Get new one")
            except jwt.InvalidTokenError:
                print("Invalid Token")
            if data['role'] == "teacher" or data['email'] == "admin@admin.com" :
                return f(*args, **kwargs)
            else:
                return jsonify({"message": "Authentication failed"})
        return decorator

    return wrapper

And login page:

@app.route('/teacherlogin', methods=['POST'])
def teacherlogin():
    email = request.form["email"]
    password = request.form["password"]
    role = request.form["role"]
    dt=datetime.now()+timedelta(seconds=120)
    try:
        user = users.find({"$and": [{"email": email}, {"role": role}]})[0]
    except IndexError:
        return jsonify({"User not found"})
    hashed_pw = user["password"]
    if bcrypt.hashpw(password.encode('utf8'), hashed_pw) == hashed_pw:
        token = jwt.encode({
            'email': email,
            'password': password,
            'role': role,
            'exp':dt
        }, app.config['SECRET_KEY'])
        res = make_response("Successful", 200)
        res.set_cookie(
            "JWT",
            value=token,
            expires=dt,
            httponly=True)
        return res
    else:
        return jsonify({"error": "Wrong password"})

I can see the expiration time in the Postman cookies section, but I can use it even after this time has passed Postman Cookies: JWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImFkbWluQGFkbWluLmNvbSIsInBhc3N3b3JkIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE2NjQyMDkyNTh9.HfanKS-2J50YteuhQg5x_gKQAOd1RPY0DDB64pyCxjU; Path=/; HttpOnly; Expires=Mon, 26 Sep 2022 16:20:58 GMT;


Solution

  • It looks like the problem is that you are using datetime.now to generate the timestamp. This returns the local time, but PyJWT requires a UTC timestamp. You can get a UTC timestamp with datetime.utcnow.


    By the way, you should not include sensitive information such as passwords in JWTs. They are only signed; they are not encrypted. This means that anyone who can see the JWT can read the sensitive information.

    For example, I used the second part of the sample JWT you posted in the following command:

    echo -n "eyJlbWFpbCI6ImFkbWluQGFkbWluLmNvbSIsInBhc3N3b3JkIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE2NjQyMDkyNTh9" | base64 -d
    

    This gave the following output:

    {"email":"admin@admin.com","password":"admin","role":"admin","exp":1664209258}
    

    This means that the password will be visible to anyone who can read the cookie, which will appear in the user's browser, maybe in server logs, and potentially will be visible on the network if the user isn't using HTTPS. (Using the Secure cookie attribute can protect against that last case.) There is also always the possibility that another vulnerability will enable an attacker to read the cookie.

    (You should probably not put emails in the JWT either, as users may change them; better to use a unique user ID like a GUID.)