Flask Jwt Extended Readthedocs Io en Stable
Flask Jwt Extended Readthedocs Io en Stable
Release 4.7.1
vimalloc rlam3
1 Installation 1
2 Basic Usage 3
6 JWT Locations 15
6.1 Headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
6.2 Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
6.3 Query String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
6.4 JSON Body . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
7 Refreshing Tokens 21
7.1 Implicit Refreshing With Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.2 Explicit Refreshing With Refresh Tokens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
7.3 Token Freshness Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
9 Configuration Options 33
9.1 Overview: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
9.2 General Options: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
9.3 Header Options: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
9.4 Cookie Options: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
9.5 Cross Site Request Forgery Options: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
9.6 Query String Options: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
9.7 JSON Body Options: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
11 Custom Decorators 43
12 API Documentation 45
12.1 Configuring Flask-JWT-Extended . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
12.2 Verify Tokens in Request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
i
12.3 Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Index 59
ii
CHAPTER
ONE
INSTALLATION
The easiest way to start working with this extension with pip:
If you want to use asymmetric (public/private) key signing algorithms, include the asymmetric_crypto extra require-
ments.
Note that if you are using ZSH (probably other shells as well), you will need to escape the brackets
If you prefer to install from source, you can clone this repo and run
1
flask-jwt-extended Documentation, Release 4.7.1
2 Chapter 1. Installation
CHAPTER
TWO
BASIC USAGE
In its simplest form, there is not much to using this extension. You use create_access_token() to make JSON Web
Tokens, jwt_required() to protect routes, and get_jwt_identity() to get the identity of a JWT in a protected
route.
app = Flask(__name__)
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token)
3
flask-jwt-extended Documentation, Release 4.7.1
if __name__ == "__main__":
app.run()
To access a jwt_required protected view you need to send in the JWT with each request. By default, this is done with
an authorization header that looks like:
{
"msg": "Missing Authorization Header"
}
HTTP/1.0 200 OK
Content-Length: 288
Content-Type: application/json
Date: Sun, 24 Jan 2021 18:10:39 GMT
Server: Werkzeug/1.0.1 Python/3.8.6
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
˓→ eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTYxMTUxMTgzOSwianRpIjoiMmI0NzliNTQtYTI0OS00ZDNjLWE4NjItZGVkZGIzODljNmVlIi
˓→UpTueBRwNLK8e-06-oo5Y_9eWbaN5T3IHwKsy6Jauaw"
$ export JWT="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
˓→eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTYxMTUxMTgzOSwianRpIjoiMmI0NzliNTQtYTI0OS00ZDNjLWE4NjItZGVkZGIzODljNmVlIi
˓→UpTueBRwNLK8e-06-oo5Y_9eWbaN5T3IHwKsy6Jauaw"
HTTP/1.0 200 OK
Content-Length: 24
Content-Type: application/json
Date: Sun, 24 Jan 2021 18:12:02 GMT
(continues on next page)
{
"logged_in_as": "test"
}
Important
Remember to change the JWT secret key in your application, and ensure that it is secure. The JWTs are signed with
this key, and if someone gets their hands on it they will be able to create arbitrary tokens that are accepted by your web
flask application.
5
flask-jwt-extended Documentation, Release 4.7.1
THREE
In most web applications it is important to have access to the user who is accessing a protected route. We provide a
couple callback functions that make this seamless while working with JWTs.
The first is user_identity_loader(), which will convert any User object used to create a JWT into a string.
On the flip side, you can use user_lookup_loader() to automatically load your User object when a JWT is present
in the request. The loaded user is available in your protected routes via current_user.
Lets see an example of this while utilizing SQLAlchemy to store our users:
app = Flask(__name__)
jwt = JWTManager(app)
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.Text, nullable=False, unique=True)
full_name = db.Column(db.Text, nullable=False)
# NOTE: In a real application make sure to properly hash and salt passwords
def check_password(self, password):
(continues on next page)
7
flask-jwt-extended Documentation, Release 4.7.1
# Register a callback function that loads a user from your database whenever
# a protected route is accessed. This should return any python object on a
# successful lookup, or None if the lookup failed for any reason (for example
# if the user has been deleted from the database).
@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"]
return User.query.filter_by(id=identity).one_or_none()
@app.route("/login", methods=["POST"])
def login():
username = request.json.get("username", None)
password = request.json.get("password", None)
user = User.query.filter_by(username=username).one_or_none()
if not user or not user.check_password(password):
return jsonify("Wrong username or password"), 401
# Notice that we are passing in the actual sqlalchemy user object here
access_token = create_access_token(identity=user)
return jsonify(access_token=access_token)
@app.route("/who_am_i", methods=["GET"])
@jwt_required()
def protected():
# We can now access our sqlalchemy User object via `current_user`.
return jsonify(
id=current_user.id,
full_name=current_user.full_name,
username=current_user.username,
)
if __name__ == "__main__":
db.create_all()
db.session.add(User(full_name="Bruce Wayne", username="batman"))
db.session.add(User(full_name="Ann Takamaki", username="panther"))
db.session.add(User(full_name="Jester Lavore", username="little_sapphire"))
db.session.commit()
HTTP/1.0 200 OK
Content-Length: 281
Content-Type: application/json
Date: Sun, 24 Jan 2021 17:23:31 GMT
Server: Werkzeug/1.0.1 Python/3.8.6
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
˓→ eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTYxMTUwOTAxMSwianRpIjoiNGFmN2ViNTAtMjk3Yy00ZmY4LWJmOTYtMTZlMDE5MWEzYzMwIi
˓→2UhZo-xo19NXaqKLwcMz0NBLAcxxEUeK4Ziqk1T_9h0"
$ export JWT="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
˓→eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTYxMTUwOTAxMSwianRpIjoiNGFmN2ViNTAtMjk3Yy00ZmY4LWJmOTYtMTZlMDE5MWEzYzMwIi
˓→2UhZo-xo19NXaqKLwcMz0NBLAcxxEUeK4Ziqk1T_9h0"
HTTP/1.0 200 OK
Content-Length: 57
Content-Type: application/json
Date: Sun, 24 Jan 2021 17:31:34 GMT
Server: Werkzeug/1.0.1 Python/3.8.6
{
"id": 2,
"full_name": "Ann Takamaki",
"username": "panther"
}
9
flask-jwt-extended Documentation, Release 4.7.1
FOUR
You may want to store additional information in the access token which you could later access in the pro-
tected views. This can be done using the additional_claims argument with the create_access_token() or
create_refresh_token() functions. The claims can be accessed in a protected route via the get_jwt() function.
It is important to remember that JWTs are not encrypted and the contents of a JWT can be trivially decoded by anyone
who has access to it. As such, you should never put any sensitive information in a JWT.
app = Flask(__name__)
@app.route("/login", methods=["POST"])
def login():
username = request.json.get("username", None)
password = request.json.get("password", None)
if username != "test" or password != "test":
return jsonify({"msg": "Bad username or password"}), 401
# In a protected view, get the claims you added to the jwt with the
# get_jwt() method
@app.route("/protected", methods=["GET"])
@jwt_required()
def protected():
(continues on next page)
11
flask-jwt-extended Documentation, Release 4.7.1
if __name__ == "__main__":
app.run()
Alternately you can use the additional_claims_loader() decorator to register a callback function that will be
called whenever a new JWT is created, and return a dictionary of claims to add to that token. In the case that both
additional_claims_loader() and the additional_claims argument are used, both results are merged together,
with ties going to the data supplied by the additional_claims argument.
FIVE
There may be cases where you want to use the same route regardless of if a JWT is present in the request or not. In
these situations, you can use jwt_required() with the optional=True argument. This will allow the endpoint to
be accessed regardless of if a JWT is sent in with the request.
If no JWT is present, get_jwt() and get_jwt_header(), will return an empty dictionary. get_jwt_identity(),
current_user, and get_current_user() will return None.
If a JWT that is expired or not verifiable is in the request, an error will be still returned like normal.
app = Flask(__name__)
@app.route("/login", methods=["POST"])
def login():
username = request.json.get("username", None)
password = request.json.get("password", None)
if username != "test" or password != "test":
return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token)
@app.route("/optionally_protected", methods=["GET"])
@jwt_required(optional=True)
def optionally_protected():
current_identity = get_jwt_identity()
if current_identity:
(continues on next page)
13
flask-jwt-extended Documentation, Release 4.7.1
if __name__ == "__main__":
app.run()
SIX
JWT LOCATIONS
JWTs can be sent in with a request in many different ways. You can control which ways you want to accept JWTs in your
Flask application via the JWT_TOKEN_LOCATION configuration option. You can also override that global configuration
on a per route basis via the locations argument in jwt_required().
app = Flask(__name__)
# Here you can globally configure all the ways you want to allow JWTs to
# be sent to your web application. By default, this will be only headers.
app.config["JWT_TOKEN_LOCATION"] = ["headers", "cookies", "json", "query_string"]
# If true this will only allow the cookies that contain your JWTs to be sent
# over https. In production, this should always be set to True
app.config["JWT_COOKIE_SECURE"] = False
jwt = JWTManager(app)
@app.route("/login_without_cookies", methods=["POST"])
def login_without_cookies():
access_token = create_access_token(identity="example_user")
return jsonify(access_token=access_token)
@app.route("/login_with_cookies", methods=["POST"])
def login_with_cookies():
response = jsonify({"msg": "login successful"})
access_token = create_access_token(identity="example_user")
set_access_cookies(response, access_token)
(continues on next page)
15
flask-jwt-extended Documentation, Release 4.7.1
@app.route("/logout_with_cookies", methods=["POST"])
def logout_with_cookies():
response = jsonify({"msg": "logout successful"})
unset_jwt_cookies(response)
return response
@app.route("/only_headers")
@jwt_required(locations=["headers"])
def only_headers():
return jsonify(foo="baz")
if __name__ == "__main__":
app.run()
Lets take a look at how you could utilize all of these locations using some javascript in a web browser.
6.1 Headers
Working JWTs via headers is a pretty simple process. All you need to do is store the token when you login, and add
the token as a header each time you make a request to a protected route. Logging out is as simple as deleting the token.
async function login() {
const response = await fetch('/login_without_cookies', {method: 'post'});
const result = await response.json();
localStorage.setItem('jwt', result.access_token);
}
function logout() {
localStorage.removeItem('jwt');
}
6.2 Cookies
Cookies are a fantastic way of handling JWTs if you are using a web browser. They offer some nice benefits compared
to the headers approach:
• They can be configured to send only over HTTPS. This prevents a JWT from accidentally being sent, and possibly
compromised, over an unsecure connection.
• They are stored in an http-only cookie, which prevents XSS attacks from being able to steal the underlying JWT.
• You Flask application can implicitly refresh JWTs that are close to expiring, which simplifies the logic of keeping
active users logged in. More on this in the next section!
Of course, when using cookies you also need to do some additional work to prevent Cross Site Request Forgery (CSRF)
attacks. In this extension we handle this via something called double submit verification.
The basic idea behind double submit verification is that a JWT coming from a cookie will only be considered valid if
a special double submit token is also present in the request, and that double submit token must not be something that
is automatically sent by a web browser (ie it cannot be another cookie).
By default, we accomplish this by setting two cookies when someone logging in. The first cookie contains the JWT,
and encoded in that JWT is the double submit token. This cookie is set as http-only, so that it cannot be access via
javascript (this is what prevents XSS attacks from being able to steal the JWT). The second cookie we set contains only
the same double submit token, but this time in a cookie that is readable by javascript. Whenever a request is made, it
needs to include an X-CSRF-TOKEN header, with the value of the double submit token. If the value in this header does
not match the value stored in the JWT, the request is kicked out as invalid.
Because the double submit token needs to be present as a header (which wont be automatically sent on a request),
and some malicious javascript running on a different domain will not be able to read the cookie containing the double
submit token on your website, we have successfully thwarted any CSRF attacks.
This does mean that whenever you are making a request, you need to manually include the X-CSRF-TOKEN header,
otherwise your requests will be kicked out as invalid too. Lets look at how to do that:
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
6.2. Cookies 17
flask-jwt-extended Documentation, Release 4.7.1
Note that there are additional CSRF options, such as looking for the double submit token in a form, changing cookie
paths, etc, that can be used to tailor things to the needs of your application. See Cross Site Request Forgery Options:
for details.
function logout() {
localStorage.removeItem('jwt');
}
// Note that if we change the method to `get` this will blow up with a
// "TypeError: Window.fetch: HEAD or GET Request cannot have a body"
async function makeRequestWithJWT() {
const options = {
method: 'post',
body: JSON.stringify({access_token: localStorage.getItem('jwt')}),
headers: {
'Content-Type': 'application/json',
},
};
const response = await fetch('/protected', options);
const result = await response.json();
return result;
}
SEVEN
REFRESHING TOKENS
In most web applications, it would not be ideal if a user was logged out in the middle of doing something because their
JWT expired. Unfortunately we can’t just change the expires time on a JWT on each request, as once a JWT is created
it cannot be modified. Lets take a look at some options for solving this problem by refreshing JWTs.
app = Flask(__name__)
# If true this will only allow the cookies that contain your JWTs to be sent
# over https. In production, this should always be set to True
app.config["JWT_COOKIE_SECURE"] = False
app.config["JWT_TOKEN_LOCATION"] = ["cookies"]
app.config["JWT_SECRET_KEY"] = "super-secret" # Change this in your code!
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=1)
jwt = JWTManager(app)
(continues on next page)
21
flask-jwt-extended Documentation, Release 4.7.1
@app.route("/login", methods=["POST"])
def login():
response = jsonify({"msg": "login successful"})
access_token = create_access_token(identity="example_user")
set_access_cookies(response, access_token)
return response
@app.route("/logout", methods=["POST"])
def logout():
response = jsonify({"msg": "logout successful"})
unset_jwt_cookies(response)
return response
@app.route("/protected")
@jwt_required()
def protected():
return jsonify(foo="bar")
if __name__ == "__main__":
app.run()
An alternative approach involves making an API request with your access token and then checking the result to see if
it worked. If the result of the request is an error message saying that your token is expired, use the refresh token to
generate a new access token and redo the request with the new token. This approach will work regardless of the clock
on your frontend, but it does require having some potentially more complicated logic.
Using refresh tokens is our recommended approach when your frontend is not a website (mobile, api only, etc).
app = Flask(__name__)
@app.route("/login", methods=["POST"])
def login():
access_token = create_access_token(identity="example_user")
refresh_token = create_refresh_token(identity="example_user")
return jsonify(access_token=access_token, refresh_token=refresh_token)
@app.route("/protected", methods=["GET"])
@jwt_required()
def protected():
return jsonify(foo="bar")
if __name__ == "__main__":
app.run()
Making a request with a refresh token looks just like making a request with an access token. Here is an example using
HTTPie.
. Warning
Note that when an access token is invalidated (e.g. logging a user out), any corresponding refresh token(s) must be
revoked too. See Revoking Refresh Tokens for details on how to handle this.
app = Flask(__name__)
# We verify the users password here, so we are returning a fresh access token
@app.route("/login", methods=["POST"])
def login():
username = request.json.get("username", None)
password = request.json.get("password", None)
if username != "test" or password != "test":
return jsonify({"msg": "Bad username or password"}), 401
# If we are refreshing a token here we have not verified the users password in
# a while, so mark the newly created access token as not fresh
@app.route("/refresh", methods=["POST"])
@jwt_required(refresh=True)
def refresh():
identity = get_jwt_identity()
access_token = create_access_token(identity=identity, fresh=False)
return jsonify(access_token=access_token)
# Only allow fresh JWTs to access this route with the `fresh=True` arguement.
@app.route("/protected", methods=["GET"])
@jwt_required(fresh=True)
def protected():
return jsonify(foo="bar")
if __name__ == "__main__":
app.run()
We also support marking a token as fresh for a given amount of time after it is created. You can do this by passing a
datetime.timedelta to the fresh option when creating JWTs:
create_access_token(identity, fresh=datetime.timedelta(minutes=15))
EIGHT
JWT revoking is a mechanism for preventing an otherwise valid JWT from accessing your routes while still let-
ting other valid JWTs in. To utilize JWT revoking in this extension, you must define a callback function via the
token_in_blocklist_loader() decorator. This function is called whenever a valid JWT is used to access a pro-
tected route. The callback will receive the JWT header and JWT payload as arguments, and must return True if the
JWT has been revoked.
In production, you will want to use some form of persistent storage (database, redis, etc) to store your JWTs. It would be
bad if your application forgot that a JWT was revoked if it was restarted. We can provide some general recommendations
on what type of storage engine to use, but ultimately the choice will depend on your specific application and tech stack.
8.1 Redis
If your only requirements are to check if a JWT has been revoked, our recommendation is to use redis. It is blazing
fast, can be configured to persist data to disc, and can automatically clear out JWTs after they expire by utilizing the
Time To Live (TTL) functionality when storing a JWT. Here is an example using redis:
import redis
from flask import Flask
from flask import jsonify
ACCESS_EXPIRES = timedelta(hours=1)
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "super-secret" # Change this!
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = ACCESS_EXPIRES
jwt = JWTManager(app)
# Setup our redis connection for storing the blocklisted tokens. You will probably
# want your redis instance configured to persist data to disk, so that a restart
# does not cause your application to forget that a JWT was revoked.
jwt_redis_blocklist = redis.StrictRedis(
host="localhost", port=6379, db=0, decode_responses=True
)
(continues on next page)
27
flask-jwt-extended Documentation, Release 4.7.1
@app.route("/login", methods=["POST"])
def login():
access_token = create_access_token(identity="example_user")
return jsonify(access_token=access_token)
# Endpoint for revoking the current users access token. Save the JWTs unique
# identifier (jti) in redis. Also set a Time to Live (TTL) when storing the JWT
# so that it will automatically be cleared out of redis after the token expires.
@app.route("/logout", methods=["DELETE"])
@jwt_required()
def logout():
jti = get_jwt()["jti"]
jwt_redis_blocklist.set(jti, "", ex=ACCESS_EXPIRES)
return jsonify(msg="Access token revoked")
# A blocklisted access token will not be able to access this any more
@app.route("/protected", methods=["GET"])
@jwt_required()
def protected():
return jsonify(hello="world")
if __name__ == "__main__":
app.run()
. Warning
Note that configuring redis to be disk-persistent is an absolutely necessity for production use. Otherwise, events
like power outages or server crashes/reboots would cause all invalidated tokens to become valid again (assuming
the secret key does not change). This is especially concering for long-lived refresh tokens, discussed below.
8.2 Database
If you need to keep track of information about revoked JWTs our recommendation is to utilize a database. This allows
you to easily store and utilize metadata for revoked tokens, such as when it was revoked, who revoked it, can it be
un-revoked, etc. Here is an example using SQLAlchemy:
app = Flask(__name__)
ACCESS_EXPIRES = timedelta(hours=1)
app.config["JWT_SECRET_KEY"] = "super-secret" # Change this!
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = ACCESS_EXPIRES
jwt = JWTManager(app)
# This could be expanded to fit the needs of your application. For example,
# it could track who revoked a JWT, when a token expires, notes for why a
# JWT was revoked, an endpoint to un-revoked a JWT, etc.
# Making jti an index can significantly speed up the search when there are
# tens of thousands of records. Remember this query will happen for every
# (protected) request,
# If your database supports a UUID type, this can be used for the jti column
# as well
class TokenBlocklist(db.Model):
id = db.Column(db.Integer, primary_key=True)
jti = db.Column(db.String(36), nullable=False, index=True)
created_at = db.Column(db.DateTime, nullable=False)
@app.route("/login", methods=["POST"])
def login():
access_token = create_access_token(identity="example_user")
(continues on next page)
8.2. Database 29
flask-jwt-extended Documentation, Release 4.7.1
# Endpoint for revoking the current users access token. Saved the unique
# identifier (jti) for the JWT into our database.
@app.route("/logout", methods=["DELETE"])
@jwt_required()
def modify_token():
jti = get_jwt()["jti"]
now = datetime.now(timezone.utc)
db.session.add(TokenBlocklist(jti=jti, created_at=now))
db.session.commit()
return jsonify(msg="JWT revoked")
# A blocklisted access token will not be able to access this any more
@app.route("/protected", methods=["GET"])
@jwt_required()
def protected():
return jsonify(hello="world")
if __name__ == "__main__":
db.create_all()
app.run()
@app.route("/logout", methods=["DELETE"])
@jwt_required(verify_type=False)
def modify_token():
token = get_jwt()
jti = token["jti"]
ttype = token["type"]
now = datetime.now(timezone.utc)
db.session.add(TokenBlocklist(jti=jti, type=ttype, created_at=now))
db.session.commit()
return jsonify(msg=f"{ttype.capitalize()} token successfully revoked")
Token type and user columns are not required and can be omitted. That being said, including these can help to audit
that the frontend is performing its revoking job correctly and revoking both tokens.
Alternatively, there are a few ways to revoke both tokens at once:
1. Send the access token in the header (per usual), and send the refresh token in the DELETE request body. This
saves a request but still needs frontend changes, so may not be worth implementing
2. Embed the refresh token’s jti in the access token. The revoke route should be authenticated with the access
token. Upon revoking the access token, extract the refresh jti from it and invalidate both. This has the advantage
of requiring no extra work from the frontend.
3. Store every generated tokens jti in a database upon creation. Have a boolean column to represent whether it is
valid or not, which the token_in_blocklist_loader should respond based upon. Upon revoking a token,
mark that token row as invalid, as well as all other tokens from the same user generated at the same time. This
would also allow for a “log out everywhere” option where all tokens for a user are invalidated at once, which is
otherwise not easily possibile
The best option of course depends and needs to be chosen based upon the circumstances. If there if ever a time where
an unknown, untracked token needs to be immediately invalidated, this can be accomplished by changing the secret
key.
NINE
CONFIGURATION OPTIONS
You can change many options for this extension works via Flask’s Configuration Handling. For example:
app.config["OPTION_NAME"] = option_value
9.1 Overview:
• General Options:
– JWT_ACCESS_TOKEN_EXPIRES
– JWT_ALGORITHM
– JWT_DECODE_ALGORITHMS
– JWT_DECODE_AUDIENCE
– JWT_DECODE_ISSUER
– JWT_DECODE_LEEWAY
– JWT_ENCODE_AUDIENCE
– JWT_ENCODE_ISSUER
– JWT_ENCODE_NBF
– JWT_ERROR_MESSAGE_KEY
– JWT_IDENTITY_CLAIM
– JWT_PRIVATE_KEY
– JWT_PUBLIC_KEY
– JWT_REFRESH_TOKEN_EXPIRES
– JWT_SECRET_KEY
– JWT_TOKEN_LOCATION
– JWT_VERIFY_SUB
• Header Options:
– JWT_HEADER_NAME
– JWT_HEADER_TYPE
• Cookie Options:
– JWT_ACCESS_COOKIE_NAME
33
flask-jwt-extended Documentation, Release 4.7.1
– JWT_ACCESS_COOKIE_PATH
– JWT_COOKIE_CSRF_PROTECT
– JWT_COOKIE_DOMAIN
– JWT_COOKIE_SAMESITE
– JWT_COOKIE_SECURE
– JWT_REFRESH_COOKIE_NAME
– JWT_REFRESH_COOKIE_PATH
– JWT_SESSION_COOKIE
• Cross Site Request Forgery Options:
– JWT_ACCESS_CSRF_COOKIE_NAME
– JWT_ACCESS_CSRF_COOKIE_PATH
– JWT_ACCESS_CSRF_FIELD_NAME
– JWT_ACCESS_CSRF_HEADER_NAME
– JWT_CSRF_CHECK_FORM
– JWT_CSRF_IN_COOKIES
– JWT_CSRF_METHODS
– JWT_REFRESH_CSRF_COOKIE_NAME
– JWT_REFRESH_CSRF_COOKIE_PATH
– JWT_REFRESH_CSRF_FIELD_NAME
– JWT_REFRESH_CSRF_HEADER_NAME
• Query String Options:
– JWT_QUERY_STRING_NAME
– JWT_QUERY_STRING_VALUE_PREFIX
• JSON Body Options:
– JWT_JSON_KEY
– JWT_REFRESH_JSON_KEY
JWT_ALGORITHM
Which algorithm to sign the JWT with. See PyJWT for the available algorithms.
Default: "HS256"
JWT_DECODE_ALGORITHMS
Which algorithms to use when decoding a JWT. See PyJWT for the available algorithms.
By default this will always be the same algorithm that is defined in JWT_ALGORITHM.
Default: ["HS256"]
JWT_DECODE_AUDIENCE
The string or list of audiences (aud) expected in a JWT when decoding it.
Default: None
JWT_DECODE_ISSUER
The issuer (iss) you expect in a JWT when decoding it.
Default: None
JWT_DECODE_LEEWAY
The number of seconds a token will be considered valid before the Not Before Time (nbf) and after the Expires
Time (`exp). This can be useful when dealing with clock drift between clients.
Default: 0
JWT_ENCODE_AUDIENCE
The string or list of audiences (aud) for created JWTs.
Default: None
JWT_ENCODE_ISSUER
The issuer (iss) for created JWTs.
Default: None
JWT_ENCODE_NBF
The not before (nbf) claim which defines that a JWT MUST NOT be accepted for processing during decode.
Default: True
JWT_ERROR_MESSAGE_KEY
The key for error messages in a JSON response returned by this extension.
Default: "msg"
JWT_IDENTITY_CLAIM
The claim in a JWT that is used as the source of identity.
Default: "sub"
JWT_PRIVATE_KEY
The secret key used to encode JWTs when using an asymmetric signing algorithm (such as RS* or ES*). The
key must be in PEM format.
Do not reveal the secret key when posting questions or committing code.
Default: None
JWT_PUBLIC_KEY
The secret key used to decode JWTs when using an asymmetric signing algorithm (such as RS* or ES*). The
key must be in PEM format.
Default: None
JWT_REFRESH_TOKEN_EXPIRES
How long a refresh token should be valid before it expires. This can be a datetime.timedelta, dateutil.relativedelta,
or a number of seconds (Integer).
If set to False tokens will never expire. This is dangerous and should be avoided in most case
This can be overridden on a per token basis by passing the expires_delta argument to flask_jwt_extended.
create_refresh_token()
Default: datetime.timedelta(days=30)
JWT_SECRET_KEY
The secret key used to encode and decode JWTs when using a symmetric signing algorithm (such as HS*). It
should be a long random string of bytes, although unicode is accepted too. For example, copy the output of this
to your config.
JWT_HEADER_NAME
What header should contain the JWT in a request
Default: "Authorization"
JWT_HEADER_TYPE
What type of header the JWT is in. If this is an empty string, the header should contain nothing besides the JWT.
Default: "Bearer"
JWT_REFRESH_COOKIE_PATH
The path for the refresh cookies
Note: We generally do not recommend using refresh tokens with cookies. See Implicit Refreshing With Cookies.
Default: "/"
JWT_SESSION_COOKIE
Controls if the cookies will be set as session cookies, which are deleted when the browser is closed.
Default: True
Default: csrf_refresh_token
JWT_REFRESH_CSRF_COOKIE_PATH
The path of the refresh CSRF double submit cookie.
Note: We generally do not recommend using refresh tokens with cookies. See Implicit Refreshing With Cookies.
Default: "/"
JWT_REFRESH_CSRF_FIELD_NAME
Name of the form field that should contain the CSRF double submit token for a refresh token. Only applicable
if JWT_CSRF_CHECK_FORM is True
Note: We generally do not recommend using refresh tokens with cookies. See Implicit Refreshing With Cookies.
Default: "csrf_token"
JWT_REFRESH_CSRF_HEADER_NAME
The name of the header on an incoming request that should contain the CSRF double submit token.
Note: We generally do not recommend using refresh tokens with cookies. See Implicit Refreshing With Cookies.
Default: "X-CSRF-TOKEN"
TEN
This extension provides sensible default behaviors. For example, if an expired token attempts to access a protected
endpoint, you will get a JSON response back like {"msg": "Token has expired"} and a 401 status code. However
there may be various behaviors of this extension that you want to customize to your application’s needs. We can do
that with the various loader functions. Here is an example of how to do that.
app = Flask(__name__)
@app.route("/login", methods=["POST"])
def login():
access_token = create_access_token("example_user")
return jsonify(access_token=access_token)
@app.route("/protected", methods=["GET"])
@jwt_required()
def protected():
return jsonify(hello="world")
41
flask-jwt-extended Documentation, Release 4.7.1
There are all sorts of callbacks that can be defined to customize the behaviors of this extension. See the Configuring
Flask-JWT-Extended API Documentation for a full list of callback functions that are available in this extension.
ELEVEN
CUSTOM DECORATORS
You can create your own decorators that extend the functionality of the decorators provided by this extension. For
example, you may want to create your own decorator that verifies a JWT is present as well as verifying that the current
user is an administrator.
flask_jwt_extended.verify_jwt_in_request() can be used to build your own decorators. This is the same
function used by flask_jwt_extended.jwt_required().
Here is an example of how this might look.
app = Flask(__name__)
# Here is a custom decorator that verifies the JWT is present in the request,
# as well as insuring that the JWT has a claim indicating that this user is
# an administrator
def admin_required():
def wrapper(fn):
@wraps(fn)
def decorator(*args, **kwargs):
verify_jwt_in_request()
claims = get_jwt()
if claims["is_administrator"]:
return fn(*args, **kwargs)
else:
return jsonify(msg="Admins only!"), 403
return decorator
43
flask-jwt-extended Documentation, Release 4.7.1
@app.route("/login", methods=["POST"])
def login():
access_token = create_access_token(
"admin_user", additional_claims={"is_administrator": True}
)
return jsonify(access_token=access_token)
@app.route("/protected", methods=["GET"])
@admin_required()
def protected():
return jsonify(foo="bar")
if __name__ == "__main__":
app.run()
TWELVE
API DOCUMENTATION
This is the documentation for all of the API that is exported in this extension.
45
flask-jwt-extended Documentation, Release 4.7.1
• optional – If True, do not raise an error if no JWT is present in the request. Defaults to
False.
• fresh – If True, require a JWT marked as fresh in order to be verified. Defaults to False.
• refresh – If True, requires a refresh JWT to access this endpoint. If False, requires an
access JWT to access this endpoint. Defaults to False
• locations – A location or list of locations to look for the JWT in this request, for example
'headers' or ['headers', 'cookies']. Defaults to None which indicates that JWTs
will be looked for in the locations defined by the JWT_TOKEN_LOCATION configuration op-
tion.
• verify_type – If True, the token type (access or refresh) will be checked according to the
refresh argument. If False, type will not be checked and both access and refresh tokens
will be accepted.
• skip_revocation_check – If True, revocation status of the token will be not checked. If
False, revocation status of the token will be checked.
Returns
A tuple containing the jwt_header and the jwt_data if a valid JWT is present in the request. If
optional=True and no JWT is in the request, None will be returned instead. Raise an exception
if an invalid JWT is in the request.
12.3 Utilities
flask_jwt_extended.create_access_token(identity: Any, fresh: bool | float | timedelta = False,
expires_delta: Literal[False] | timedelta | None = None,
additional_claims=None, additional_headers=None)
Create a new access token.
Parameters
• identity – The identity of this token. This must either be a string, or you must have defined
user_identity_loader() in order to convert the object you passed in into a string.
• fresh – If this token should be marked as fresh, and can thus access endpoints protected
with @jwt_required(fresh=True). Defaults to False.
This value can also be a datetime.timedelta, which indicate how long this token will be
considered fresh.
• expires_delta – A datetime.timedelta for how long this token should last be-
fore it expires. Set to False to disable expiration. If this is None, it will use the
JWT_ACCESS_TOKEN_EXPIRES config value (see Configuration Options)
• additional_claims – Optional. A hash of claims to include in the access token. These
claims are merged into the default claims (exp, iat, etc) and claims returned from the
additional_claims_loader() callback. On conflict, these claims take precedence.
• headers – Optional. A hash of headers to include in the access token. These
headers are merged into the default headers (alg, typ) and headers returned from the
additional_headers_loader() callback. On conflict, these headers take precedence.
Returns
An encoded access token
flask_jwt_extended.create_refresh_token(identity: Any, expires_delta: Literal[False] | timedelta | None =
None, additional_claims=None, additional_headers=None)
12.3. Utilities 49
flask-jwt-extended Documentation, Release 4.7.1
Returns
The current user object for the JWT in the current request
flask_jwt_extended.get_jti(encoded_token: str) → str | None
Returns the JTI (unique identifier) of an encoded JWT
Parameters
encoded_token – The encoded JWT to get the JTI from.
Returns
The JTI (unique identifier) of a JWT, if it is present.
flask_jwt_extended.get_jwt() → dict
In a protected endpoint, this will return the python dictionary which has the payload of the JWT that is accessing
the endpoint. If no JWT is present due to jwt_required(optional=True), an empty dictionary is returned.
Returns
The payload (claims) of the JWT in the current request
flask_jwt_extended.get_jwt_header() → dict
In a protected endpoint, this will return the python dictionary which has the header of the JWT that is accessing
the endpoint. If no JWT is present due to jwt_required(optional=True), an empty dictionary is returned.
Returns
The headers of the JWT in the current request
flask_jwt_extended.get_jwt_identity() → Any
In a protected endpoint, this will return the identity of the JWT that is accessing the endpoint. If no JWT is
present due to jwt_required(optional=True), None is returned.
Returns
The identity of the JWT in the current request
flask_jwt_extended.get_unverified_jwt_headers(encoded_token: str) → dict
Returns the Headers of an encoded JWT without verifying the signature of the JWT.
Parameters
encoded_token – The encoded JWT to get the Header from.
Returns
JWT header parameters as python dict()
flask_jwt_extended.set_access_cookies(response: Response, encoded_access_token: str, max_age=None,
domain=None) → None
Modifiy a Flask Response to set a cookie containing the access JWT. Also sets the corresponding CSRF cookies
if JWT_CSRF_IN_COOKIES is True (see Configuration Options)
Parameters
• response – A Flask Response object.
• encoded_access_token – The encoded access token to set in the cookies.
• max_age – The max age of the cookie. If this is None, it will use the JWT_SESSION_COOKIE
option (see Configuration Options). Otherwise, it will use this as the cookies max-age and
the JWT_SESSION_COOKIE option will be ignored. Values should be the number of sec-
onds (as an integer).
• domain – The domain of the cookie. If this is None, it will use the JWT_COOKIE_DOMAIN
option (see Configuration Options). Otherwise, it will use this as the cookies domain and
the JWT_COOKIE_DOMAIN option will be ignored.
12.3. Utilities 51
flask-jwt-extended Documentation, Release 4.7.1
THIRTEEN
This release includes a lot of breaking changes that have been a long time coming, and will require some manual
intervention to upgrade your application. Breaking changes are never fun, but I really believe they are for the best.
As a result of all these changes, this extension should be simpler to use, provide more flexibility, and allow for easier
additions to the API without introducing further breaking changes. Here is everything you will need to be aware of
when upgrading to 4.0.0.
53
flask-jwt-extended Documentation, Release 4.7.1
@jwt.revoked_token_loader
def revoked_token_response(jwt_header, jwt_payload):
return jsonify(msg=f"I'm sorry {jwt_payload['sub']} I can't let you do that")
• The arguments for @jwt.decode_key_loader have been reversed to be consistent with the rest of the ap-
plication. Previously the arguments were (jwt_payload, jwt_headers). Now they are (jwt_headers,
jwt_payload).
f
flask_jwt_extended, 45
57
flask-jwt-extended Documentation, Release 4.7.1
A I
additional_claims_loader() init_app() (flask_jwt_extended.JWTManager method),
(flask_jwt_extended.JWTManager method), 45 46
additional_headers_loader() invalid_token_loader()
(flask_jwt_extended.JWTManager method), 45 (flask_jwt_extended.JWTManager method), 46
C J
create_access_token() (in module JWT_ACCESS_COOKIE_NAME (built-in variable), 37
flask_jwt_extended), 49 JWT_ACCESS_COOKIE_PATH (built-in variable), 37
create_refresh_token() (in module JWT_ACCESS_CSRF_COOKIE_NAME (built-in variable), 38
flask_jwt_extended), 49 JWT_ACCESS_CSRF_COOKIE_PATH (built-in variable), 38
current_user (in module flask_jwt_extended), 50 JWT_ACCESS_CSRF_FIELD_NAME (built-in variable), 38
JWT_ACCESS_CSRF_HEADER_NAME (built-in variable), 38
D JWT_ACCESS_TOKEN_EXPIRES (built-in variable), 34
decode_key_loader() JWT_ALGORITHM (built-in variable), 34
(flask_jwt_extended.JWTManager method), 45 JWT_COOKIE_CSRF_PROTECT (built-in variable), 37
decode_token() (in module flask_jwt_extended), 50 JWT_COOKIE_DOMAIN (built-in variable), 37
JWT_COOKIE_SAMESITE (built-in variable), 37
E JWT_COOKIE_SECURE (built-in variable), 37
JWT_CSRF_CHECK_FORM (built-in variable), 38
encode_key_loader()
JWT_CSRF_IN_COOKIES (built-in variable), 38
(flask_jwt_extended.JWTManager method), 45
JWT_CSRF_METHODS (built-in variable), 38
expired_token_loader()
JWT_DECODE_ALGORITHMS (built-in variable), 35
(flask_jwt_extended.JWTManager method), 46
JWT_DECODE_AUDIENCE (built-in variable), 35
F JWT_DECODE_ISSUER (built-in variable), 35
JWT_DECODE_LEEWAY (built-in variable), 35
flask_jwt_extended JWT_ENCODE_AUDIENCE (built-in variable), 35
module, 45 JWT_ENCODE_ISSUER (built-in variable), 35
JWT_ENCODE_NBF (built-in variable), 35
G JWT_ERROR_MESSAGE_KEY (built-in variable), 35
get_csrf_token() (in module flask_jwt_extended), 50 JWT_HEADER_NAME (built-in variable), 36
get_current_user() (in module flask_jwt_extended), JWT_HEADER_TYPE (built-in variable), 37
50 JWT_IDENTITY_CLAIM (built-in variable), 35
get_jti() (in module flask_jwt_extended), 51 JWT_JSON_KEY (built-in variable), 39
get_jwt() (in module flask_jwt_extended), 51 JWT_PRIVATE_KEY (built-in variable), 35
get_jwt_header() (in module flask_jwt_extended), 51 JWT_PUBLIC_KEY (built-in variable), 35
get_jwt_identity() (in module flask_jwt_extended), JWT_QUERY_STRING_NAME (built-in variable), 39
51 JWT_QUERY_STRING_VALUE_PREFIX (built-in variable),
get_unverified_jwt_headers() (in module 39
flask_jwt_extended), 51 JWT_REFRESH_COOKIE_NAME (built-in variable), 37
JWT_REFRESH_COOKIE_PATH (built-in variable), 38
59
flask-jwt-extended Documentation, Release 4.7.1
M
module
flask_jwt_extended, 45
N
needs_fresh_token_loader()
(flask_jwt_extended.JWTManager method), 46
R
revoked_token_loader()
(flask_jwt_extended.JWTManager method), 46
S
set_access_cookies() (in module
flask_jwt_extended), 51
set_refresh_cookies() (in module
flask_jwt_extended), 51
T
token_in_blocklist_loader()
(flask_jwt_extended.JWTManager method), 47
token_verification_failed_loader()
(flask_jwt_extended.JWTManager method), 47
token_verification_loader()
(flask_jwt_extended.JWTManager method), 47
U
unauthorized_loader()
(flask_jwt_extended.JWTManager method), 47
unset_access_cookies() (in module
flask_jwt_extended), 52
unset_jwt_cookies() (in module flask_jwt_extended),
52
unset_refresh_cookies() (in module
flask_jwt_extended), 52
user_identity_loader()
(flask_jwt_extended.JWTManager method), 47
60 Index