Search code examples
pythonflaskflask-loginflask-principal

Flask Login and Principal - current_user is Anonymous even though I'm logged in


I'm using Flask Login and Principal for identity and role management. My needs are described straight out of the docs. My code is here:

@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
    # Set the identity user object
    identity.user = current_user

    # Add the UserNeed to the identity
    if hasattr(current_user, 'get_id'):
        print 'current_user ' + str(current_user.get_id())
        identity.provides.add(UserNeed(current_user.get_id))

    # Assuming the User model has a list of roles, update the
    # identity with the roles that the user provides
    if hasattr(current_user, 'roles'):
        if current_user.roles:
            for role in current_user.roles:
                identity.provides.add(RoleNeed(role.name))

In my login code I do this:

identity_changed.send(current_app._get_current_object(),
                                  identity=Identity(user.user_id)

On login, the signal fires as expected. On each subsequent page load, the current_user is anonymous and doesn't have the user id yet all @login_required functions behave as if the user is logged in. Flask login knows that the user is logged in but for some reason the current_user is inconsistent.

Am I missing an essential point of configuration somewhere?


Solution

  • I encountered the same problem! The root cause is that both Flask-Login and Flask-Principal are invoked by Flask during the "preprocess" stage of the request in the order that they were registered with your Flask app. If you register Flask-Principal before you register Flask-Login, then @identity_loaded.connect_via(app) will be called before @login_manager.user_loader, and therefore current_user will return the anonymous user.

    The Flask-Principal documentation example shows a code excerpt where Flask-Principal is registered before Flask-Login. Tsk tsk! Here's what I ended up doing in my bootstrap:

    login_manager = LoginManager()
    login_manager.init_app(app)
    
    # ...
    
    principals = Principal(app) # This must be initialized after login_manager.
    

    Then in my users.py view file:

    @identity_loaded.connect_via(app)
    def on_identity_loaded(sender, identity):
        """ This function is called by Flask-Principal after a user logs in. """
    
        identity.user = current_user
    
        if isinstance(current_user, User):
            identity.provides.add(UserNeed(current_user.id))
    
        for permission in user.permissions:
            # Do permission-y stuff here.
    

    This solved the problem for me.

    Edit: I submitted a bug report to the project for the documentation.