ibrewster ibrewster - 11 months ago 96
Python Question

flask: get session time remaining via AJAX

I am using Flask, with the flask-session plugin for server-side sessions stored in a Redis backend. I have flask set up to use persistent sessions, with a session timeout. How can I make an AJAX request to get the time remaining on the session without resetting the timeout?

The idea is for the client to check with the server before displaying a timeout warning (or logging out the user) in case the user is active in a different tab/window of the same browser.

EDIT: after some digging, I found the config directive

, which it appears I should be able to use to accomplish what I want: set that to False, and then the session should only be refreshed if something actually changes in the session, so I should be able to make a request to get the timeout without the session timeout changing. It was added in 0.11, and I'm running 0.11.1, so it should be available.

Unfortunately, in practice this doesn't appear to work - at least when checking the
of the redis key to get the time remain. I checked, and
is False, so it's not just that I am doing something in the request that modifies the session (unless it just doesn't set that flag)

Answer Source

The following works, though it is rather hacky:

In the application __init__.py, or wherever you call Session(app) or init_app(app):

#set up the session

# Save a reference to the original save_session function so we can call it
original_save_session = app.session_interface.save_session

def discretionary_save_session(self, *args, **kwargs):
    """A wrapper for the save_session function of the app session interface to
    allow the option of not saving the session when calling specific functions,
    for example if the client needs to get information about the session
    (say, expiration time) without changing the session."""

    # bypass: list of functions on which we do NOT want to update the session when called
    # This could be put in the config or the like
    # Improvement idea: "mark" functions on which we want to bypass saving in
    # some way, then check for that mark here, rather than having a hard-coded list.
    bypass = ['check_timeout']

    #convert function names to URL's
    bypass = [flask.url_for(x) for x in bypass]

    if not flask.request.path in bypass:
        # if the current request path isn't in our bypass list, go ahead and
        # save the session normally
        return original_save_session(self, *args, **kwargs)

# Override the save_session function to ours
app.session_interface.save_session = discretionary_save_session

Then, in the check_timeout function (which is in the bypass list, above), we can do something like the following to get the remaining time on the session:

def check_timeout():
    session_id = flask.session.sid

    # Or however you want to get a redis instance
    redis = app.config.get('REDIS_MASTER')

    # If used
    session_prefix = app.config.get('SESSION_KEY_PREFIX')

    #combine prefix and session id to get the session key stored in redis
    redis_key = "{}{}".format(session_prefix, session_id)

    # The redis ttl is the time remaining before the session expires
    time_remain = redis.ttl(redis_key)

    return str(time_remain)

I'm sure the above can be improved upon, however the result is as desired: when calling /auth/check_timeout, the time remaining on the session is returned without modifying the session in any way.