简体   繁体   中英

Flask-Socketio out of sync with Flask-Login's current_user

Python==3.8.5
Flask==1.1.2
Flask-SocketIO=4.3.0 (also tried 4.3.1)
Flask_Login=0.4.1

I use gevent with Gunicorn

gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker w 1 wsgi:server

I have been using Flask-Login for a couple years now, and for about a year, I have managed to make it work with as an authentication mechanism for a RESTful app with a React frontend. The problem I am having lays within the use with Flask-socketio. Although I manage to sign-out successfully, the current_user within flask_socketio endpoints still holds the current_user object as valid, and even more bizarre is when I re-login with another user, the current_user in the global Flask app reflects the logouts and new logins, but the current_user within flask_socketio loses sync with the changes. I have been searching for quite some time now, and I found a blog post from Miguel Grinberg, the author of flask_socketio where he states:

The Flask session is copied to the Socket.IO session at the time of the Socket.IO connection. Changes made to the session in Flask routes after the Socket.IO connection was made will not be accessible on the Socket.IO session. Changes made to the session in Socket.IO event handlers will not be accessible on the Flask user session.

Flask Login support: you can reference current_user in your Socket.IO event handlers without any problems.

It is true I could reference and access the current_user from socket.io, but it's always the initial value which does not reflects the changes that it may be subject to during its life time.

Can I somehow access the current_user accessible through out the the whole app (including regular http requests) in the socket.io context?

Here is a regular http example:

@auth_bp.route('/current_user/<int:id>')
def user_is_authenticated(id):
    if id:
        if current_user.is_authenticated:
            if current_user.id == int(id):
                return {
                            "code": 200,
                            "authenticated": True,
                            "message":"User is logged-in", 
                            "user": user_schema.dump(current_user),
                        }, 200
    return {
                "code": 400,
                "authenticated": False,
                "message":"Either user id missing or you are not authenticated.", 
                "user": None
            }, 400

If I hit the above endpoint from the client side, I get the actual current_user. And below is a socketio endpoint for the same purpose:

@socket.on('getCurrentUser', namespace='/auth')
def user_is_authenticated(data):
    id = data.get('id')
    if id:
        if current_user.is_authenticated:
            if current_user.id == int(id):
                user = user_schema.dump(current_user)
                emit('currentUser', {'user': user, 'room': user.room}, room=user.room)
    #emit event here to sid

Mocking authentication actions

#login as Adam Clark

current_user across Flask now is {
    fullname: 'Adam Clark',
    other_fields: 'data'
}

current_user in socketio context also is {
    fullname: 'Adam Clark',
    other_fields: 'data'
}

# logout

current_user across Flask now is AnonymousUser

current_user in socketio context also is still {
    fullname: 'Adam Clark',
    other_fields: 'data'
}


# login as John Wild

current_user across Flask now is {
    fullname: 'John Wild',
    other_fields: 'data'
}

current_user in socketio context also is still {
    fullname: 'Adam Clark',
    other_fields: 'data'
}

The only difference between the results of both is that the http one (Flask endpoint) reflects the current state of current_user, whereas, the socket.io, maintains only the initial state of the object.

This is an unfortunate limitation when working with cookie based sessions and Socket.IO. The problem is that if an HTTP route makes a change to the session (such as logging out with Flask-Login), there is no way to tell the WebSocket connection that the session cookie has changed, since WebSocket does not have any support for cookies outside of the initial connection request.

Documentation link: https://flask-socketio.readthedocs.io/en/latest/#access-to-flask-s-context-globals .

If you need the session to be synchronized between the HTTP and WebSocket sides, then use server-side sessions. The Flask-Session extension is a good choice for this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM