How to make Flask remember a login session

6.4k Views Asked by At

I'm creating a flask server with Flask-Login like this:

Logging in works (it prints the correct user). Right after this, I make a test request. It always returns an anonymous user and a different one each time (expecting to see the one who just logged in).

The requests are made by a html page on a http server on the same (local) ip but a different port than the flask server.

This is my python file, using crossdomainflask:

from flask import *
from flask.ext.login import (LoginManager, UserMixin, login_user, current_user)
from crossdomainflask import *

class User(UserMixin):
    def __init__(self, username):
        self.name = username

    @property
    def id(self):
        return self.name

class FlaskServer(object):
    def __init__(self):
        self.login_manager = LoginManager()
        self.server = Flask(__name__)
        self.server.secret_key = '123456'       
        self.server.config.update(PROPAGATE_EXCEPTIONS = True)
        self.login_manager.init_app(self.server)

        @self.login_manager.user_loader
        def load_user(userid):
            return User("test")

        @self.server.route('/api/login', methods=['POST'])
        @crossdomain(origin='*')
        def login():
            user = load_user(request.values.get('username'))
            if user:
                login_user(user)
                print(current_user)
                print(current_user.name)
                return 'ok'

        @self.server.route("/api/test", methods=['POST', 'GET'])        
        @crossdomain(origin='*')
        def test():
            print(current_user)
            return 'ok'

        self.server.run("0.0.0.0")
server = FlaskServer()
2

There are 2 best solutions below

1
On BEST ANSWER

flask-login seems not to work with crossdomainflask. Solved it by serving static files with the same flask server.

0
On

If you read the docs for Flask-Login, you'll see that you need to define a user loader which reloads the user on subsequent requests after they are logged in.

@login_manager.user_loader
def user_loader(id):
    # do whatever you need to to load the user object
    # a database query, for example
    user = MyUser.query.get(id)
    return user

The issue may also be that crossdomain doesn't behave correctly with the login cookie set by Flask-Login. You may need to change the cookie's domain or other security features to allow this. However, this is not the right way to go about allowing logins from other sites.


There are other things wrong with the code you've posted.

There is no need for a server class, all the patterns in Flask are designed to work at a module level just fine. All state can be held in the app and its config, and proxied with current_app.

The development server (app.run()) is for local development only. When you want to deploy in production, use a real application server such as uWSGI or Gunicorn, and a real web server such as Nginx or Apache. They provide infinitely more performance and security benefits than the development server.

It's not bad to expose an API, but it is bad to expose login directly. The way you've set it up, the remote site must collect and send the credentials. Consider setting up an OAuth provider, so that only your site handles credentials. The Stack Exchange API provides a good example of this workflow, and there are various Flask extensions, such as Flask-OAuthlib, that implement this.

You should not hash passwords with MD5, it has been incredibly insecure for a decade now. Use a real hashing implementation such as PBKDF2, BCrypt, or SCrypt. Passlib provides an excelent interface to these algorithms, along with the ability to upgrade hashes over time.