Flask JWT extend validity of token on each request
Asked Answered
M

2

14

Scenario

A logged in user will have a token expiry of 24 hours. Within that period, all request with @jwt_required decorator will have the current access token's expiry extended by another 24 hours. There is a maximum validity of 168(24 * 7) hours.

It is possible to use access_token and refresh_token.

ret = {
        'access_token': create_access_token(identity=username, fresh=True),
        'refresh_token': create_refresh_token(identity=username)
    }

But that means every API call from my applicatino will be two requests: 1. Actual HTTP Request 2. Refresh the auth token

@app.route('/refresh', methods=['POST'])
@jwt_refresh_token_required
def refresh():
    current_user = get_jwt_identity()
    ret = {
        'access_token': create_access_token(identity=current_user)
    }
    return jsonify(ret), 200

Is there a way to implicitly extend an auth token?

Mumps answered 13/9, 2017 at 12:2 Comment(0)
S
38

EDIT: There is now documentation around this here: https://flask-jwt-extended.readthedocs.io/en/stable/refreshing_tokens.html

Author of flask-jwt-extended here. Technically you cannot actually extend a token, you can only replace it with a new JWT that has a new expires time. There are a few ways you could simulate this though.

First, instead of having the client request a new token, you could have the server itself just implicitly send back a new token on every request. You could send the new JWTs back in a header instead of in the JSON payload, so that you wouldn't have to modify you JSON data to account for the possibility of a new JWT. Your clients would need to be aware of this though, they would need to check for that new header on every request and replace their current JWT with the new one if it is present. You could probably use a flask after_request method to do this, so you didn't have to add that functionality to all your endpoints. A similar effect could be achieved when storing the JWTs in cookies, with the differences being that cookies are automatically stored in your browser (so your client wouldn't have to manually look for them on every request), and with the added complexity of CSRF protection if you go this route (http://flask-jwt-extended.readthedocs.io/en/latest/tokens_in_cookies.html).

The above should work fine, but you will be creating a lot of access tokens that are thrown away right after being created, which probably isn't ideal. A variation of the above is to check if the token is near expiring (maybe if it is more then half way to being expired) and only create and return a new token if that is the case. Another variation of this would be to have the client check if the token is about to expire (via javascript) and if it is, use the refresh token to request a new access token. To do that, you would need to split the JWT on dots ('.'), base64 decode the second set of strings from that split (index 1), and grab the 'exp' data from there.

A second way you could do this is actually wait for a token to expire, and then use the refresh token to generate a new access token and remake the request (reactive instead of proactive). That might look like making a request, checking if the http code is 401, if so use the refresh token to generate a new access token, then making the request again.

Hope this helps :)

Sturdy answered 18/9, 2017 at 16:59 Comment(6)
Hi thanks for this comment, I'm using your library and got a question. So I'm currently using the double submit cookie method, and in this case only the csrf_access_token and csrf_refresh_token is accessible by JavaScript and these are not JWT, how can I retrieve the JWT in front-end and get the expiration time in order for me to persist the user's identity in the client side by checking the expiration time?Fixing
From the fronted you can not access the actual JWT cookie with CSRF protection enabled. For security reasons those cookies are set with the http only flag. You would need to either send that information back when a token is created and save it on your frontend or create an api endpoint that decodes the token on your backend and sends back the exp timestamp that your frontend can call out to.Sturdy
Thank you for your reply. Do you think the client-side making an extra request to get the expiration time is better than an extra request to just generate a new token? I'm just trying to be logical here because if you have to make an extra call anyway for accessing any protected view, what would be a better approach since both of the scenarios would have to make an extra call anyway.Fixing
I'm bit hesitant to store the expiration info into either local storage of as another set of cookie because it can be vulnerable to either XSS or CSRF since cookie will be stored in non-http only so that JS can retrieve it.Fixing
I would not make an extra api call every time, I would only do it once (when initially generating a new token for example). There shouldn’t be any problem about storing the exp time locally as that does not contain any sensitive information. If someone gets their hands on that they cannot do anything malicious with it, it’s just a timestamp.Sturdy
Thanks for your insight. I really appreciate it.Fixing
J
7
app = Flask(__name__)

app.config["JWT_SECRET_KEY"] = "super-secret"  # Change this!
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=1)
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=30)
jwt = JWTManager(app)

change time according to your requirement

Jimmiejimmy answered 1/10, 2021 at 7:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.