python - How to limit one session from any browser for a username in flask? -
i using gunicorn server in trying figure out way limit 1 session per username i.e. if user logged in app chrome should not able login through firefox unless logs out of chrome, or shouldn`t able open tab in chrome itself.
how can generate unique id browser , store in db until user logs out or session expires, user can`t login through other browser.
a possible method of limiting sessions single tab involves creating random token on page load , embedding token page. generated token gets stored in user's session well. similar how various frameworks add validation tokens prevent csfr attacks.
brief example:
- user loads page in tab 1 in firefox.
token1
generated, embedded , stored in session - user loads page in tab 2 in firefox.
token2
generated, embedded , stored in session. overwrites previous value. - user loads page in tab 1 in chrome.
token3
generated, embedded , stored in session. overwrites previous value.
at point, user has page open in 3 tabs. user's session, though, has token3
stored. method prevents user being locked out (different ip addresses, different user agent strings, incogneto mode, etc) because each new session generates new token. newest load becomes active window, invalidating previous sessions.
next, time page interacts server (clicks link, submits data, etc.), token embedded in page sent well. server validates passed token matches token in session. if match, action succeeds. if not match, server returns failure message.
you can generate random numbers in multiple ways, want secure. we'll use example question:
import string import random ... n = 20 # length of string want, adjust appropriate ''.join(random.systemrandom().choice(string.ascii_uppercase + string.digits) _ in range(n))
this uses random.systemrandom
, more secure using random.choice
on page load, need check if existing token valid, generate random token , store in user's session. since want everywhere, let's make decorator first, reduce duplicate code later. decorator checks if session valid , if not select (insert own logic). sets session token. needed (or need logic exclude main page) otherwise you'll hit infinite loop user attempts load main page, doesn't have token, fails , process repeats. have token regenerating on each page load via else
clause. if not implement if
section, decorator pointless both paths perform same action , reset token on page load. logic in if
prevent user having multiple sessions.
from flask import session functools import wraps def random_string(length): return ''.join(random.systemrandom().choice(string.ascii_uppercase + string.digits) _ in range(length)) def validate_token(f): @wraps(f) def wrapper(existing_token, *args, **kwargs): if session['token'] != existing_token: # logic on failure. present 404, bounce them main page, do else? # important determine , implement logic # otherwise decorator changes token (and behaves same way else block). session['token'] = random_string(20) else: session['token'] = random_string(20) return f(*args, **kwargs) return wrapper
now in our routes, can apply decorator each, user session gets updated on each page load:
from flask import render_template @app.route('/path') @validate_token def path(token=none): return render_template('path.html', token=session['token'])
in template, want utilize token
value anywhere need prevent session continuing. example, put on links, in forms (though flask has method of csrf protection already), etc. server can check if passed token valid. template simple this:
<a href="{{ url_for('path', token=token) }}">click here continue</a>
Comments
Post a Comment