Heroku gunicorn flask login is not working properly

Question:

I have a flask app that uses Flask-Login for authentication. Everything works fine locally both using flask’s built in web server and gunicorn run locally. But when it’s on heroku it’s faulty, sometimes it logs me in and sometimes it does not. When I successfully logged in within a few seconds of navigating my session just gets destroyed and have me logged out automatically. This should just happen when the user logged out.

The following code snippet in my view might be relevant:

@app.before_request
def before_request():
    g.user = current_user

# I have index (/) and other views (/new) decorated with @login_required

I might be having similar issues with this. It does not have any answers yet and from what I read from the comments, the author just ran his app with python app.py. That is through using flask’s built-in web server. However I can’t seem to duplicate his workaround since running app.run(host='0.0.0.0') runs the app in port 5000 and I can’t seem to set port=80 because of permission.

I don’t see anything helpful with the logs except that it does not authenticate even when I should.

Part of the logs when I got authenticated and tried to navigate to /new and / alternately until it logs me out:

2016-09-25T06:57:53.052378+00:00 app[web.1]: authenticated - IP:10.179.239.229
2016-09-25T06:57:53.455145+00:00 heroku[router]: at=info method=GET path="/" host=testdep0.herokuapp.com request_id=c7c8f4c9-b003-446e-92d8-af0a81985e72 fwd="124.100.201.61" dyno=web.1 connect=0ms service=116ms status=200 bytes=6526
2016-09-25T06:58:11.415837+00:00 heroku[router]: at=info method=GET path="/new" host=testdep0.herokuapp.com request_id=ae5e4e29-0345-4a09-90c4-36fb64785079 fwd="124.100.201.61" dyno=web.1 connect=0ms service=7ms status=200 bytes=2552
2016-09-25T06:58:13.543098+00:00 heroku[router]: at=info method=GET path="/" host=testdep0.herokuapp.com request_id=47696ab9-57b9-4f20-810a-66033e3e9e50 fwd="124.100.201.61" dyno=web.1 connect=0ms service=8ms status=200 bytes=5982
2016-09-25T06:58:18.037766+00:00 heroku[router]: at=info method=GET path="/new" host=testdep0.herokuapp.com request_id=98912601-6342-4d71-a106-26056e4bbb21 fwd="124.100.201.61" dyno=web.1 connect=0ms service=3ms status=200 bytes=2552
2016-09-25T06:58:19.619369+00:00 heroku[router]: at=info method=GET path="/" host=testdep0.herokuapp.com request_id=2b04d31f-93a2-4653-83a4-f95ca9b97149 fwd="124.100.201.61" dyno=web.1 connect=0ms service=3ms status=302 bytes=640
2016-09-25T06:58:19.953910+00:00 heroku[router]: at=info method=GET path="/login?next=%2F" host=testdep0.herokuapp.com request_id=e80d15cd-e9ad-45ff-ae54-e156412fe4ff fwd="124.100.201.61" dyno=web.1 connect=0ms service=3ms status=200 bytes=2793

Procfile:

web: gunicorn app:app

Asked By: krato

||

Answers:

The problem was solved by adding the --preload option to gunicorn. I’m not entirely sure how that solved the problem and would appreciate if someone can explain.

Updated Procfile:

web: gunicorn app:app --preload

Answered By: krato

Following up with what krato said (which got it to work, at all), my app still was not working perfectly. Even if it logged in the first time I could keep refreshing and eventually would be logged out. So after reading this article I set gunicorn to use only 1 worker like so:

web: gunicorn -w 1 :app –preload

which seemed to do the trick.

Answered By: ndeagon

Just adding some pointers for anyone who came here indulging in this issue

This issue may arise because of a multithreaded environment in the production

Before processing any request context flask-login query load_user so you have to customize this according to your production environment

you have to implement something like the below, this is up to you how to give a user object to flask-login before processing of request context

@login_manager.user_loader
def load_user(id):
    app.logger.debug(f"session {session}")
    if session:
        return User.query.filter_by(id=session['_user_id']).first() 

check how exactly _load_user works as this has been called to load current_user
https://github.com/maxcountryman/flask-login/blob/main/src/flask_login/login_manager.py#L301

check this in the documentation
https://flask-login.readthedocs.io/en/latest/#how-it-works

check this maybe you just need to add remember me in cookie
https://github.com/maxcountryman/flask-login/blob/main/src/flask_login/login_manager.py#L331

Answered By: Prince Roshan

I had a similar issue with session problems. It was also causing CSRF problems for me which lead me to this question: CSRF Token Error when using gunicorn and Flask

Basically app.app_context().push() was causing the problem.

Answered By: dt00