-
-
Notifications
You must be signed in to change notification settings - Fork 247
Description
Hi,
I'm upgrading from 3.x to 4.1.0 and I'm running into RuntimeError: You must provide a @jwt.user_lookup_loader callback to use this method. I'm getting this error when loading my login page which has a view of:
@user.route('/login')
def login():
return render_template('user/login.html')Here's the route I took to try and narrow down the error:
- If I change the above to
return 'hey'then the exception isn't thrown. - If I go into my
login.htmltemplate and make thebodyblock empty, then the exception is thrown. - If I goto my
login.htmltemplate and don't pull in my layout then the exception isn't thrown. - If I goto my layout template and remove my nav bar (which reads
current_user.username) then the exception isn't thrown and I'm able to login and everything works.
So there's something about reading that current_user that used to work in 3.x that's not working anymore in 4.x and it's causing things to blow up. The code in the template is href="{{ url_for('facts.index', username=current_user.username) }}". This works when I'm logged in but not when I'm logged out.
It used to work in both cases with 3.x. I am using JS to toggle the visibility of some menu items based on being logged in or not. I know I can likely alter the logic to check if there's a current_user at the Jinja level but I'm trying to streamline the UI a bit with JS and if a bad actor unhid the nav items as a logged out user it wouldn't do any harm. Any thoughts on how to get this to work with 4.x?
Here's what I've done so far:
- Renamed
@jwt_requiredto@jwt_required()in every spot - Followed everything in https://flask-jwt-extended.readthedocs.io/en/stable/v4_upgrade_guide/ to the best of my ability
In my old 3.x code, I had this in my callbacks:
def jwt_callbacks():
@jwt.user_loader_callback_loader
def user_loader_callback(identity):
return User.query.filter((User.username == identity)).first()In the new 4.x code, I've changed that to:
def jwt_callbacks():
@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data['sub']
return User.query.filter((User.username == identity)).first()In both cases this jwt_callbacks() function is being called in my create_app() function which is the set up I was using with 3.x that worked.
As for using the required decorators, this all worked in the 3.x code using this code style:
@user.route('/community')
@jwt_required
def community():
return render_template('user/community.html')In the 4.x upgrade I'm doing the same ordering except now I'm calling jwt_required(). In the cases where I am using optional authentication I've changed those to jwt_required(optional=True) as per the upgrade guide.
Here's the full stack trace which is interesting because it's saying I'm trying to use get_current_user() but the callback isn't defined, however that callback is defined.
web_1 | [2021-03-25 23:06:29 +0000] [27] [ERROR] Error handling request /login
web_1 | Traceback (most recent call last):
web_1 | File "/home/python/.local/lib/python3.9/site-packages/gunicorn/workers/sync.py", line 134, in handle
web_1 | self.handle_request(listener, req, client, addr)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/gunicorn/workers/sync.py", line 175, in handle_request
web_1 | respiter = self.wsgi(environ, resp.start_response)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/app.py", line 2464, in __call__
web_1 | return self.wsgi_app(environ, start_response)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/app.py", line 2450, in wsgi_app
web_1 | response = self.handle_exception(e)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/app.py", line 1867, in handle_exception
web_1 | reraise(exc_type, exc_value, tb)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/_compat.py", line 39, in reraise
web_1 | raise value
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/app.py", line 2447, in wsgi_app
web_1 | response = self.full_dispatch_request()
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/app.py", line 1952, in full_dispatch_request
web_1 | rv = self.handle_user_exception(e)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/app.py", line 1821, in handle_user_exception
web_1 | reraise(exc_type, exc_value, tb)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/_compat.py", line 39, in reraise
web_1 | raise value
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/app.py", line 1950, in full_dispatch_request
web_1 | rv = self.dispatch_request()
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask_debugtoolbar/__init__.py", line 125, in dispatch_request
web_1 | return view_func(**req.view_args)
web_1 | File "/app/fakefacts/blueprints/user/views.py", line 9, in login
web_1 | return render_template('user/login.html')
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/templating.py", line 137, in render_template
web_1 | return _render(
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask/templating.py", line 120, in _render
web_1 | rv = template.render(context)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/jinja2/environment.py", line 1090, in render
web_1 | self.environment.handle_exception()
web_1 | File "/home/python/.local/lib/python3.9/site-packages/jinja2/environment.py", line 832, in handle_exception
web_1 | reraise(*rewrite_traceback_stack(source=source))
web_1 | File "/home/python/.local/lib/python3.9/site-packages/jinja2/_compat.py", line 28, in reraise
web_1 | raise value.with_traceback(tb)
web_1 | File "/app/fakefacts/blueprints/user/templates/user/login.html", line 2, in top-level template code
web_1 | {% import 'macros/form.html' as f with context %}
web_1 | File "/app/fakefacts/templates/layouts/base.html", line 52, in top-level template code
web_1 | <a class="nav-link" href="{{ url_for('facts.index', username=current_user.username) }}">
web_1 | File "/home/python/.local/lib/python3.9/site-packages/jinja2/environment.py", line 471, in getattr
web_1 | return getattr(obj, attribute)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/werkzeug/local.py", line 347, in __getattr__
web_1 | return getattr(self._get_current_object(), name)
web_1 | File "/home/python/.local/lib/python3.9/site-packages/werkzeug/local.py", line 306, in _get_current_object
web_1 | return self.__local()
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask_jwt_extended/utils.py", line 10, in <lambda>
web_1 | current_user = LocalProxy(lambda: get_current_user())
web_1 | File "/home/python/.local/lib/python3.9/site-packages/flask_jwt_extended/utils.py", line 77, in get_current_user
web_1 | raise RuntimeError(
web_1 | RuntimeError: You must provide a `@jwt.user_lookup_loader` callback to use this method
As for settings, the only ones I've set are:
JWT_TOKEN_LOCATION = ['cookies', 'headers']
JWT_COOKIE_SECURE = False
JWT_SESSION_COOKIE = False
JWT_ACCESS_TOKEN_EXPIRES = timedelta(weeks=52)
JWT_ACCESS_COOKIE_PATH = '/'
JWT_COOKIE_CSRF_PROTECT = TrueAny ideas?