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 |
---|---|
Function that is called to verify the user_claims data. Must return True or False |
|
Function that is called when the user claims verification callback returns False |
|
Function that is called to get the decode key before verifying a token |
|
Function that is called to get the encode key before creating a token |
|
Function to call when an expired token accesses a protected endpoint |
|
Function to call when an invalid token accesses a protected endpoint |
|
Function to call when a non-fresh token accesses a |
|
Function to call when a revoked token accesses a protected endpoint |
|
Function that is called to check if a token has been revoked |
|
Function to call when a request with no JWT accesses a protected endpoint |
|
Function to call to load a user object when token accesses a protected endpoint |
|
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).