2
votes

I would like to have the following authentication scheme

  • a login page where the user login with email and password, the flask server then returns a JWT token
  • for later API access, the user has to provide the JWT token in the authorization header

However, I don't fully understand the inner working of flask-login. My preliminary understanding is that

  • After user submits the password, we call login_user and flask-login creates a session to store user information (maybe user_id?)
  • Next time a request comes, authentication is done before processing the request if login_required decorator is used

I thought only request_loader would be needed if JWT authentication is used because all we need to do is check every request header. But if only the request_loader function is provided (see code below), exception is thrown.

Exception: No user_loader has been installed for this LoginManager. Add one with the 'LoginManager.user_loader' decorator.

More specifically, my questions are

  1. what are the roles played by user_loader and request_loader if we are to use JWT authentication?
  2. when are user_loader and request_loader being called, if both of them are provided?
  3. why do we still need user_loader if request_loader is provided?

Here are my implementation of these two loaders

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@login_manager.request_loader
def load_user_from_request(request):

    auth_str = request.headers.get('Authorization')
    token = auth_str.split(' ')[1] if auth_str else ''
    if token:
        user_id = User.decode_token(token)
        user = User.query.get(int(user_id))
        if user:
            return user
    return None
1

1 Answers

2
votes

I notice the exception of

Exception: No user_loader has been installed for this LoginManager. Add one with the 'LoginManager.user_loader' decorator.

is caused by the the logout handler given as follows

@auth.route('/logout')
@login_required
def logout():
    logout_user()
    flash('You have logged out.')
    return redirect(url_for('home.index'))

The reason is that flask-login needs to figure out which user to log out, which is done by calling a utils._get_user() function, which calls login_manager._load_user(), which further calls login_manager.reload_user(), and reload_user() will try to call the function login_manager.user_loader decorates which converts the user_id stored in the session to a user instance.

Thus as a workaround, one can define this function login_manager.user_loader decorates. I still think it's kind of a bug because the request alone is able to determine the user instance already, isn't that the purpose of request_loader?