Search code examples
session-cookiesflask-session

Flask-Session: How to uniquely identify between the different user's session data on a single browser


I wrote a small flask app for a shopping cart and user login. I used the flask-session extension and sqlite3 for the

The problem I faced. I logged in as user A, and I added some items to the cart. Then I logged out from the app and logged in as another user B from the same browser. I did not close the browser. Now when I checked the cart, I could see the items that user A had added to his cart.

This is not what I expected, essentially I should have got an empty cart.

Kindly let me know what mistake I made.

I am pasting the code below that I used.

from flask import Flask, render_template, redirect, url_for, request, session
from flask_session import Session

import sqlite3


app = Flask(__name__)
app.config['SECRET_KEY'] = "ASecretKey-1"
app.config['SESSION_PERMANENT'] = True
app.config['SESSION_TYPE'] = 'filesystem'


Session(app)


def connectDB():
    dbObj = sqlite3.connect('shoppingCart.db')
    dbObj.row_factory = sqlite3.Row
    cur = dbObj.cursor()
    return (dbObj, cur)

def disConnectDB(dbObj, cur):
    cur.close()
    dbObj.close()
    
    del cur, dbObj





@app.route('/')
@app.route('/home')
def home():
    if not session.get('userName'):
        return render_template('login.html', pageTitle='Login Page')

    dbObj, cur = connectDB()
    data = cur.execute('SELECT rowid, * FROM inventory').fetchall()
    disConnectDB(dbObj, cur)
    contextValues=dict(pageTitle='HomePage', data=data, userName=session.get('userName'))

    return render_template('home.html', **contextValues)



@app.get('/login')
@app.post('/login')
def login():
    userName = request.form.get('userName')
    password = request.form.get('password')

    if (not userName) or (not password):
        return "<h1 style='color:darkorange'>Form incomplete.</h1>"
    else:
        dbObj, cur = connectDB()
        data = cur.execute("SELECT * FROM users WHERE userName =? AND password=?", (userName,password,)).fetchone()
        disConnectDB(dbObj, cur)

        print("*"*25,data)

        if data:
            session['userName'] = userName
            if not session.get('cart'):
                session['cart'] = []
        else:
            return "<h1 style='color:darkorange'>Incorrect Credentials.</h1>"
        

    return redirect(url_for('home'))




@app.route('/logout')
def logout():
    session.pop('userName')
    return redirect(url_for('home'))



@app.route('/cart')
def cart():
    cart = session.get('cart')
    return render_template('cart.html',pageTitle='cart',cart=cart)


@app.route('/addtocart', methods=['GET','POST'])
def addtocart():
    if session.get('userName'):
        qty = int(request.form.get('qty'))
        itemID = request.form.get('itemID')
        dbObj, cur = connectDB()
        itemData = cur.execute("SELECT * FROM inventory WHERE rowid=?",(itemID,)).fetchone()
        

        itemName= itemData['Item']
        price = itemData['Price']
        available_quantity=itemData['Quantity']

        if available_quantity - qty >= 0:
            available_quantity = available_quantity-qty
            session['cart'].append([itemName,qty,price*qty])
            cur.execute("UPDATE inventory SET Quantity=? WHERE rowid=?",(available_quantity,itemID,))
            dbObj.commit()
            disConnectDB(dbObj, cur)
            return render_template('addtocart.html', pageTitle='Added to cart', itemName=itemName, quantity=qty)
        else:
            disConnectDB(dbObj, cur)
            return "<h1 style='color:darkorange'>Insufficent quantity! Item cannot be added!</h1><br><a href='/home'>Return to home page</a>"
    else:
        return "<h1 style='color:darkorange'>You are not authorized to visit this page!</h1><a href='/home'>Return to login page</a>"

Solution

  • When a user uses your /logout route, you just clear the userName key from the session, but the cart is still stored in the session. The session is tied to the browser, and it will retain whatever is left in it. When the same browser logs in later, the cart data is still in the session.

    The simplest, safest way to fix that is to call session.clear() any time the session should be invalidated. That will remove all data from the session, giving you a clean slate. I would do that in the /logout route and at the beginning of any successful login attempt (before setting the userName and initial cart list). If you don't clear the session on login, then a logged in user could go to the login page without logging out, log in as a different user, and have the first user's cart still in the session. That is unlikely to happen, but it shouldn't be possible in any case.