flask-jwt icon indicating copy to clipboard operation
flask-jwt copied to clipboard

Refresh/Extend token life

Open HelloGrayson opened this issue 11 years ago • 30 comments

Thoughts?

HelloGrayson avatar Oct 04 '14 10:10 HelloGrayson

I also need this feature (maybe it's already there), actually we are using jwt for mobile app and it's not very convenient to login again after N days.

FindBoat avatar Oct 15 '14 15:10 FindBoat

No thoughts on this yet. For the time being you can adjust the JWT_EXPIRATION_DELTA

mattupstate avatar Oct 27 '14 19:10 mattupstate

+1

pgorsira avatar Nov 16 '14 19:11 pgorsira

Currently exploring jwt-flask as part of a backend for a mobile application and also wonder what is the best practice for refreshing tokens...

How would you suggest to implement the refresh token? Should it be a second JWT with a long lifetime? The problem here is though, how do you invalidate the token if the user does not want to allow you further access?

Another option would be to let the user of the library deal with the verification of refresh tokens by having to specify a creation/verification function ..

patrickjahns avatar Feb 22 '15 15:02 patrickjahns

+1

minhoryang avatar May 02 '15 08:05 minhoryang

+1

hafenr avatar May 02 '15 12:05 hafenr

Why refresh and not request a new one? What would be the point of expiration if all you do is extend it?

rlam3 avatar May 02 '15 13:05 rlam3

Requesting a new one forces the user to log in again.. Unless you implement a refresh token flow

patrickjahns avatar May 02 '15 15:05 patrickjahns

+1

sureshjoshi avatar May 05 '15 23:05 sureshjoshi

@patrickjahns i just have my other end of the api decrypt the token getting the expiration date and request a new one if its expired... is that sufficient?

rlam3 avatar May 06 '15 01:05 rlam3

That results in a huge security issue. An attacker can then intercept any token from your api and receive a valid new one by replaying previous tokens...

Your approach renders any expiration useless and you might as well set the expiration to several years, saving yourself the trouble of even checking.

Search around for jwt + refresh token for a secure approach to this issue. It would be nice if flask-jwt would provide the ability to easily implement that flow, but there needs to be some rewriting.

Also it feels that it is abandoned right now, since no pull requests are merged, neither are improvements comment nor is the pypy package updated. So for implementing the work flow of recommend writing your own small plugin inspired by flask-jwt On May 6, 2015 3:58 AM, "rlam3" [email protected] wrote:

@patrickjahns https://github.com/patrickjahns i just have my other end of the api decrypt the token getting the expiration date and request a new one if its expired... is that sufficient?

— Reply to this email directly or view it on GitHub https://github.com/mattupstate/flask-jwt/issues/29#issuecomment-99286543 .

patrickjahns avatar May 06 '15 11:05 patrickjahns

I saw an implement here Blacklisting JSON Web Token API Keys

bangive avatar Jun 22 '15 10:06 bangive

not sure you guys are talking about refresh before expiration or after expiration.

if refresh before expiration, we can do something like this, depending on the client to send the request.

from flask.ext.jwt import verify_jwt, current_user @app.route('/auth/refresh', methods=['get']) def refresh_token(): verify_jwt() payload = jwt.payload_callback(current_user) new_token = jwt.encode_callback(payload) return jwt.response_callback(new_token)

s4turnight avatar Jun 30 '15 03:06 s4turnight

@s4turnight i think your code does not delete the old token so the old token can still be used.

bangive avatar Jul 01 '15 01:07 bangive

Where exactly are the tokens anyways? How does this module know where to store them? Is there a way to return a list of tokens?

rlam3 avatar Jul 01 '15 12:07 rlam3

+1

raptoria avatar Oct 14 '15 17:10 raptoria

For now I use Fresh-Token header in my API responses so client should not call /auth endpoint every time it's expired but on each response has new fresh token generated on backend. So in this way I just set the token to expire in 3 minutes but if user is still active in the client-side application, it will receive fresh token in responses and have an experience of "continuous session". And additionally you don't need to log-out user since the token's lifetime is too short. So if no any activity in 3 minutes, the token will be expired and the "session" will die. And in such scenarios I would like to have some public method provided by the interface of the JWT instance to make able to just call jwt.generate_token() and easily get fresh token, maybe using the old one which is not expired yet.

What do you think about such stuff in the public interface?

Update.

For now I have to use a workaround to retrieve fresh token which looks like:

from flask import current_app
from flask_jwt import current_user
from flask_restful import Resource


def generate_token(user):
    """ Currently this is workaround
    since the latest version that already has this function
    is not published on PyPI yet and we don't want
    to install the package directly from GitHub.
    See: https://github.com/mattupstate/flask-jwt/blob/9f4f3bc8dce9da5dd8a567dfada0854e0cf656ae/flask_jwt/__init__.py#L145
    Hope it will be released on PyPI soon.
    https://github.com/mattupstate/flask-jwt/issues/38#issuecomment-127167806
    """
    jwt = current_app.extensions['jwt']
    payload = jwt.payload_callback(user)
    token = jwt.encode_callback(payload)
    return token


class BaseResource(Resource):
    def dispatch_request(self, *args, **kwargs):
        response = super(BaseResource, self).dispatch_request(*args, **kwargs)
        if current_user:
            response[2]['Fresh-Token'] = generate_token(current_user)
        return response

f0t0n avatar Oct 23 '15 15:10 f0t0n

+1

vimalloc avatar Oct 27 '15 02:10 vimalloc

+1

hussaintamboli avatar Dec 29 '15 12:12 hussaintamboli

inspired by the answer from @f0t0n, but without using flask-restful.

I made a decorator that would require and reissue a new token, to be used anywhere I'd normally use @jwt_required()

def jwt_required_refresh(func):
    @jwt_required()
    def wrapper(*args, **kwargs):
        token = jwt.jwt_encode_callback(current_identity)
        resp = make_response(func(*args, **kwargs))
        resp.headers['Fresh-Token'] = token
        return resp
    return wrapper

mtourne avatar Apr 17 '16 00:04 mtourne

@mtourne In the absence of OAuth-style refresh token support, I like this approach but I'm unclear on how you would use it to decorate a handler. I guess the func arg in your decorator is the handler function? Could you add a (trivial) example?

howesb avatar Apr 27 '16 09:04 howesb

Updated version :

def jwt_required_refresh(realm=None):
    def wrapper(fn):
        @wraps(fn)
        @jwt_required(realm=realm)
        def decorator(*args, **kwargs):
            token = jwt.jwt_encode_callback(current_identity)
            resp = make_response(fn(*args, **kwargs))
            resp.headers['Fresh-Token'] = token
            return resp
        return decorator
    return wrapper

Example:

@app.route("/api/v0/todo_list", methods=['GET'])
@jwt_required_refresh()
def get_todo_list():
    pass

mtourne avatar May 15 '16 00:05 mtourne

when using flask-jwt... should i need to send API Key every time too? or how does API key work with flask-jwt in this case?

rlam3 avatar May 17 '16 17:05 rlam3

+1

yaoelvon avatar May 25 '16 05:05 yaoelvon

@mtourne Based on your example, when the token expires due to inactivity, how would you refresh the token? Thanks!

rlam3 avatar Sep 07 '16 12:09 rlam3

I would like to see this feature. It would be nice to have something built in where I could generate long lived refresh tokens (that only worked to get normal tokens, not to have access to the entire endpoints), and then use the short-lived normal tokens for consuming the api.

vimalloc avatar Sep 13 '16 17:09 vimalloc

After reading stormpath's approach and several other publications it seems like the best way to refresh the JWT is to provide a "refresh_token" during authentication and every time a new "access_token" is given to client side.


# This should be the ideal way to provide the token
# authentication / refreshed token payload

{
"access_token": "....etc...",
"refresh_token": "...etc..."
}

The refresh_token can be used later to provide to server side to be consumed and provide a new "acess_token" again with a "newer refresh_token".

Would love some feedback on this approach. Thanks!

Reference: http://stackoverflow.com/questions/33645173/token-based-authentication-from-a-mobile-app https://grokswift.com/alamofire-OAuth2/ https://www.raywenderlich.com/99431/oauth-2-with-swift-tutorial https://stormpath.com/blog/build-note-taking-app-swift-ios

rlam3 avatar Sep 19 '16 15:09 rlam3

I've created a new flask-jwt extension which provides optional refresh tokens and optional revoking/blacklisting tokens baked in. It also has the idea of 'fresh' tokens, so when you first login you can access some critical views with your token (such as resetting a password), but subsequent tokens created with the refresh token wouldn't be able to access that view, until they re-authed and got a new 'fresh' token.

You can view it here: https://github.com/vimalloc/flask-jwt-extended/

vimalloc avatar Sep 19 '16 16:09 vimalloc

@vimalloc Awesome man! Thanks I'll definitely be following your jwt-extended.

rlam3 avatar Sep 19 '16 16:09 rlam3

@rlam3 I haven't really implemented that part but in that example, you'd basically have the client check the token for expiration periodically and if the expiration time is close you'd hit the endpoint that triggers a token refresh.

Alternatively you could ask the user's input with some kind of "you're about to get logged out yes/no" ui dialogue, if you wanted to conditionally refresh the token.

mtourne avatar Sep 24 '16 18:09 mtourne