Changing Default Behaviors

Changing callback functions

We provide what we think are sensible behaviors when attempting to access a protected endpoint. If the access token is not valid for any reason (missing, expired, tampered with, etc) we will return json in the format of {‘msg’: ‘why accessing endpoint failed’} along with an appropriate http status code (generally 401 or 422). However, you may want to customize what you return in some situations. We can do that with the jwt_manager loader functions.

from quart import Quart, jsonify, request
from quart_jwt_extended import JWTManager, jwt_required, create_access_token

app = Quart(__name__)

app.config["JWT_SECRET_KEY"] = "super-secret"  # Change this!
jwt = JWTManager(app)


# Using the expired_token_loader decorator, we will now call
# this function whenever an expired but otherwise valid access
# token attempts to access an endpoint
@jwt.expired_token_loader
def my_expired_token_callback(expired_token):
    token_type = expired_token["type"]
    return (
        {
            "status": 401,
            "sub_status": 42,
            "msg": "The {} token has expired".format(token_type),
        },
        401,
    )


@app.route("/login", methods=["POST"])
async def login():
    username = (await request.get_json()).get("username", None)
    password = (await request.get_json()).get("password", None)
    if username != "test" or password != "test":
        return {"msg": "Bad username or password"}, 401

    ret = {"access_token": create_access_token(username)}
    return ret, 200


@app.route("/protected", methods=["GET"])
@jwt_required
async def protected():
    return {"hello": "world"}, 200


if __name__ == "__main__":
    app.run()

Here are the possible loader functions. Click on the links for a more more details about what arguments your callback functions should expect and what the return values of your callback functions need to be.

Loader Decorator

Description

claims_verification_loader()

Function that is called to verify the user_claims data. Must return True or False

claims_verification_failed_loader()

Function that is called when the user claims verification callback returns False

decode_key_loader()

Function that is called to get the decode key before verifying a token

encode_key_loader()

Function that is called to get the encode key before creating a token

expired_token_loader()

Function to call when an expired token accesses a protected endpoint

invalid_token_loader()

Function to call when an invalid token accesses a protected endpoint

needs_fresh_token_loader()

Function to call when a non-fresh token accesses a fresh_jwt_required() endpoint

revoked_token_loader()

Function to call when a revoked token accesses a protected endpoint

token_in_blacklist_loader()

Function that is called to check if a token has been revoked

unauthorized_loader()

Function to call when a request with no JWT accesses a protected endpoint

user_loader_callback_loader()

Function to call to load a user object when token accesses a protected endpoint

user_loader_error_loader()

Function that is called when the user_loader callback function returns None

Dynamic token expires time

You can also change the expires time for a token via the expires_delta kwarg in the create_refresh_token() and create_access_token() functions. This takes a datetime.timedelta and overrides the JWT_REFRESH_TOKEN_EXPIRES and JWT_ACCESS_TOKEN_EXPIRES settings (see Configuration Options).

This can be useful if you have different use cases for different tokens. For example, you might use short lived access tokens used in your web application, but you allow the creation of long lived access tokens that other developers can generate and use to interact with your api in their programs. You could accomplish this like such:

@app.route('/create-dev-token', methods=['POST'])
@jwt_required
def create_dev_token():
    username = get_jwt_identity()
    expires = datetime.timedelta(days=365)
    token = create_access_token(username, expires_delta=expires)
    return jsonify({'token': token}), 201

You can even disable expiration by setting expires_delta to False:

@app.route('/create-api-token', methods=['POST'])
@jwt_required
def create_api_token():
    username = get_jwt_identity()
    token = create_access_token(username, expires_delta=False)
    return jsonify({'token': token}), 201

Note that in this case, you should enable token revoking (see Blacklist and Token Revoking).