Python Flask: keeping track of user sessions? How to get Session Cookie ID?
Question: 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.

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'))
    


Answer: You can access request cookies through the [`request.cookies`
dictionary](http://flask.pocoo.org/docs/0.10/api/#flask.Request.cookies) 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](http://flask.pocoo.org/docs/0.10/api/#flask.Response.set_cookie):

    
    
    @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_](http://stackoverflow.com/a/1594484/135978) 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`](http://flask.pocoo.org/docs/0.10/quickstart/#sessions)
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'])
    

