0

Using: Flask, jwt_extended

Background

As my code/test project has grown, I've split it into several routes, and have run into a problem with custom overrides for jwt_extended.

These routes are loaded from:

**File:  .../__init__.py** (project root)

...
from app.routes import default, user, post, ...
...

I have successfully used the changing default behavior outlined in https://flask-jwt-extended.readthedocs.io/en/stable/changing_default_behavior/

to override jwt_extended's default, for example expired tokens.

Problem:

The @jwt.xyz_loader overrides need to be in the same file as the routes they're being used on. This creates code duplication, and I'm sure it shouldn't need to be like this.

Question:

How can the @jwt.xyz_loader functions overrides be loaded from a separate file?

Code samples - working (when @jwt.xyz_loader is in the same file as the route it's being used with, here via @jwt_required:

**File:  .../routes/default.py**    

...
from flask_jwt_extended import JWTManager,, jwt_required, current_user, unset_jwt_cookies, ...
jwt = JWTManager(app)

@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
    identity = jwt_data["sub"]
    return User.query.filter_by(public_id=identity).one_or_none()

@jwt.invalid_token_loader
def invalid_token_loader(jwt_data):
    flash('Please log in.')
    response = redirect(url_for('login', next=request.path))
    unset_jwt_cookies(response)
    return response
...    
@app.route("/profile/", methods=['GET'])
@jwt_required(optional=True)
def profile():
...

Tried:

Importing jwt from a file, where the @jwt.xyz_loaders are with the JWT when it's defined. But these fail to override the default behavior when the JWT is used.

**File:  .../auth/web.py**

from flask_jwt_extended import jwt_required, current_user, JWTManager, set_access_cookies, unset_jwt_cookies, get_jwt, decode_token
jwt = JWTManager(app)

def user_lookup_callback(_jwt_header, jwt_data):
    identity = jwt_data["sub"]
    return User.query.filter_by(public_id=identity).one_or_none()

@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
    identity = jwt_data["sub"]
    return User.query.filter_by(public_id=identity).one_or_none()

@jwt.expired_token_loader
def my_expired_token_callback(jwt_header, jwt_payload):

...

**File:  .../routes/default.py**   

from app.auth.web import jwt

...    
@app.route("/profile/", methods=['GET'])
@jwt_required(optional=True)
def profile():
...

The JWT is loaded, but without the @jwt.xyz_loader overrides defined in auth.py.

Also tried:

Using jwt.init_app(app) in init.py in project root where routes, db etc are loaded. Even if defined here, @jwt.xyz_loader functions do not override jwt_extended default behaviors. These still can only be overridden when referencing @jwt.xyz_loader in the same route as the JWT is being used, for example @jwt_required.

Question: (restating, in case lost in the middle above)

How can the @jwt.xyz_loader function jwt_extended default behavior overrides be loaded from a separate file?

  • If anyone happens to come across this: – utc_extended May 11 '22 at 11:48
  • If anyone happens to come across this, not solved as intended, but a solution nonetheless: Use 'g' for current_user. Define callback functions as you'd like in a dedicated file, where flask_jwt_extended et al are imported. Include a @before_request function with g.current_user = current_user. If, as me, you're seeking different callback overrides for different routes (e.g. web, api), call a different file. Good enough. – utc_extended May 11 '22 at 11:57

0 Answers0