I have built a small website with Flask, Flask-login and FLask-sqlalchemy with user login.
When in production:
To be noted that I have the same behavior with two different servers: pythonanywhere and O2switch. So, the problem should be on my side :/ (or the 2 servers have a wrong configurations ?)
In local, I have no issue (it is difficult to have the same conditions as in production with another device but let say that I have no issue when using Incognito mode of my browser as the second user).
The code I am using:
__init__.py
# library
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
# modules
from mywebsite_flask.config import config_selection
# init extensions
db = SQLAlchemy()
login_manager = LoginManager()
# app set-up
def create_app(config_mode):
app = Flask(__name__)
app.config.from_object(config_selection[config_mode])
# flask-login init
login_manager.login_view = "auth_bp.login"
login_manager.init_app(app)
# database init
db.init_app(app)
from mywebsite_flask.auth.models import Customer
with app.app_context():
db.create_all()
# add blueprints
from mywebsite_flask.public.routes import public_bp
app.register_blueprint(public_bp)
from mywebsite_flask.auth.routes import auth_bp
app.register_blueprint(auth_bp)
app.app_context().push()
return app
models.py
# library
from flask_login import UserMixin
# modules
from mywebsite_flask import db, login_manager
class Customer(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
email = db.Column(db.String, unique=True)
password = db.Column(db.String)
token_verification = db.Column(db.Boolean, default=False)
def __repr__(self):
return f"Customer #{self.id}:{self.email}>"
@login_manager.user_loader
def load_user(customer_id): # customer found by unique id
return Customer.query.get(int(customer_id))
auth.routes.py
# library
from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required, login_user, logout_user
from werkzeug.security import generate_password_hash
# modules
from mywebsite_flask import db
from mywebsite_flask.auth.models import Customer
# init
auth_bp = Blueprint("auth_bp", __name__)
@auth_bp.route("/login", methods=("GET", "POST"))
def login():
if not current_user.is_authenticated:
if request.method == "POST":
email = request.form.get("email").lower()
password = request.form.get("password")
remember = True if request.form.get("remember") else False
check_user = db.session.execute(
db.select(Customer).filter_by(email=email)
).scalar_one_or_none()
if not various_check(check_user):
# check that none of them are None
error_msg = "Un des champs requis est vide."
flash(error_msg, category="error")
else:
login_user(check_user, remember=remember)
return redirect(url_for("public_bp.home"))
return render_template("auth/login.html")
else:
return redirect(url_for("core_bp.customer_home"))
@auth_bp.route("/logout", methods=("GET", "POST"))
@login_required
def logout():
logout_user()
return redirect(url_for("public_bp.home"))
# end
I have reviewed my code several times, looked at flask-login documentation and read some similar stackoverflow posts without being able to find a solution. Can someone already have the issue or see a problem with my code ?
Edit
Please note that, when the problem is triggered: if user B connects after user A login, we see the session of user A but there is no cookie present on user B side (and still, we can see and use user A session).
OK, I have found the problem.
In the app factory create_app() in init.py, I had app.app_context().push()
I have added it as a shortcut at the beginning of the project. It was to easily access app.config parameters everywhere in the application.
The problem was solved by:
app.app_context().push()
current_app
(if possible) or with app.app_context()
(if not), to access app.config (my original need)app.app_context().push()
somehow interferes with the different sessions created on server (and more specifically on the current_user
from flask login)
(thanks @relent95 for your help)