Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Flask: keeping track of user sessions? How to get Session Cookie ID?

I want to build a simple webapp as part of my learning activity. Webapp is supposed to ask for user to input their email_id if it encounters a first time visitor else it remembers the user through cookie and automatically logs him/her in to carry out the functions.

This is my first time with creating a user based web app. I have a blue print in my mind but I am unable to figure out how to implement it. Primarily I am confused with respect to the way of collecting user cookie. I have looked into various tutorials and flask_login but I think what I want to implement is much simpler as compared to what flask_login is implementing.

I also tried using flask.session but it was a bit difficult to understand and I ended up with a flawed implementation.

Here is what I have so far (it is rudimentary and meant to communicate my use case):

from flask import render_template, request, redirect, url_for   @app.route("/", methods= ["GET"]) def first_page():   cookie = response.headers['cookie']   if database.lookup(cookie):    user = database.get(cookie) # it returns user_email related to that cookie id    else:     return redirect_url(url_for('login'))   data = generateSomeData() # some function   return redirect(url_for('do_that'), user_id, data, stats)  @app.route('/do_that', methods =['GET']) def do_that(user_id):   return render_template('interface.html', user_id, stats,data) # it uses Jinja template  @app.route('/submit', methods =["GET"]) def submit():   # i want to get all the information here   user_id = request.form['user_id']# some data   answer = request.form['answer'] # some response to be recorded   data = request.form['data'] # same data that I passed in do_that to keep    database.update(data,answer,user_id)   return redirect(url_for('/do_that'))  @app.route('/login', methods=['GET']) def login():   return render_template('login.html')  @app.route('/loggedIn', methods =['GET']) def loggedIn():   cookie = response.headers['cookie']   user_email = response.form['user_email']   database.insert(cookie, user_email)   return redirect(url_for('first_page')) 
like image 411
pg2455 Avatar asked Sep 17 '15 21:09

pg2455


People also ask

Does Flask session use cookies?

The cookie used to store session data is known session cookie. However, unlike an ordinary cookie, Flask Cryptographically signs the session cookie. It means that anyone can view the contents of the cookie, but can't modify the cookie unless he has the secret key used to sign the cookie.

How session is maintained in Flask?

The default session is implemented using secure cookies. Cookies are persisted by the client's browser, Flask doesn't do anything in that regard. Each client has a unique session cookie, which it sends to the Flask server with each request.

Does Flask login use session?

By default, Flask-Login uses sessions for authentication. This means you must set the secret key on your application, otherwise Flask will give you an error message telling you to do so. See the Flask documentation on sessions to see how to set a secret key.


1 Answers

You can access request cookies through the request.cookies dictionary and set cookies by using either make_response or just storing the result of calling render_template in a variable and then calling set_cookie on the response object:

@app.route("/") def home():     user_id = request.cookies.get('YourSessionCookie')     if user_id:         user = database.get(user_id)         if user:             # Success!             return render_template('welcome.html', user=user)         else:             return redirect(url_for('login'))     else:         return redirect(url_for('login'))  @app.route("/login", methods=["GET", "POST"]) def login():     if request.method == "POST":         # You should really validate that these fields         # are provided, rather than displaying an ugly         # error message, but for the sake of a simple         # example we'll just assume they are provided          user_name = request.form["name"]         password = request.form["password"]         user = db.find_by_name_and_password(user_name, password)          if not user:             # Again, throwing an error is not a user-friendly             # way of handling this, but this is just an example             raise ValueError("Invalid username or password supplied")          # Note we don't *return* the response immediately         response = redirect(url_for("do_that"))         response.set_cookie('YourSessionCookie', user.id)         return response  @app.route("/do-that") def do_that():     user_id = request.cookies.get('YourSessionCookie')     if user_id:         user = database.get(user_id)         if user:             # Success!             return render_template('do_that.html', user=user)         else:             return redirect(url_for('login'))     else:         return redirect(url_for('login')) 

DRYing up the code

Now, you'll note there is a lot of boilerplate in the home and do_that methods, all related to login. You can avoid that by writing your own decorator (see What is a decorator if you want to learn more about them):

from functools import wraps from flask import flash  def login_required(function_to_protect):     @wraps(function_to_protect)     def wrapper(*args, **kwargs):         user_id = request.cookies.get('YourSessionCookie')         if user_id:             user = database.get(user_id)             if user:                 # Success!                 return function_to_protect(*args, **kwargs)             else:                 flash("Session exists, but user does not exist (anymore)")                 return redirect(url_for('login'))         else:             flash("Please log in")             return redirect(url_for('login'))     return wrapper 

Then your home and do_that methods get much shorter:

# Note that login_required needs to come before app.route # Because decorators are applied from closest to furthest # and we don't want to route and then check login status  @app.route("/") @login_required def home():     # For bonus points we *could* store the user     # in a thread-local so we don't have to hit     # the database again (and we get rid of *this* boilerplate too).     user = database.get(request.cookies['YourSessionCookie'])     return render_template('welcome.html', user=user)  @app.route("/do-that") @login_required def do_that():     user = database.get(request.cookies['YourSessionCookie'])     return render_template('welcome.html', user=user) 

Using what's provided

If you don't need your cookie to have a particular name, I would recommend using flask.session as it already has a lot of niceties built into it (it's signed so it can't be tampered with, can be set to be HTTP only, etc.). That DRYs up our login_required decorator even more:

# You have to set the secret key for sessions to work # Make sure you keep this secret app.secret_key = 'something simple for now'   from flask import flash, session  def login_required(function_to_protect):     @wraps(function_to_protect)     def wrapper(*args, **kwargs):         user_id = session.get('user_id')         if user_id:             user = database.get(user_id)             if user:                 # Success!                 return function_to_protect(*args, **kwargs)             else:                 flash("Session exists, but user does not exist (anymore)")                 return redirect(url_for('login'))         else:             flash("Please log in")             return redirect(url_for('login')) 

And then your individual methods can get the user via:

user = database.get(session['user_id']) 
like image 84
Sean Vieira Avatar answered Sep 29 '22 09:09

Sean Vieira