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()
.
from flask import Flask
from flask import jsonify
from flask_jwt_extended import create_access_token
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager
from flask_jwt_extended import set_access_cookies
from flask_jwt_extended import unset_jwt_cookies
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
# Change this in your code!
app.config["JWT_SECRET_KEY"] = "super-secret"
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)
return response
@app.route("/logout_with_cookies", methods=["POST"])
def logout_with_cookies():
response = jsonify({"msg": "logout successful"})
unset_jwt_cookies(response)
return response
@app.route("/protected", methods=["GET", "POST"])
@jwt_required()
def protected():
return jsonify(foo="bar")
@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.
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');
}
async function makeRequestWithJWT() {
const options = {
method: 'post',
headers: {
Authorization: `Bearer ${localStorage.getItem('jwt')}`,
}
};
const response = await fetch('/protected', options);
const result = await response.json();
return result;
}
Query String¶
You can also send in the JWT as part of the query string. However, It is very important to note that in most cases we recommend NOT doing this. It can lead to some non-obvious security issues, such as saving the JWT in a browsers history or the JWT being logged in your backend server, which could both potentially lead to a compromised token. However, this feature might provide some limited usefulness, such as sending password reset links, and therefore we support it in this extension.
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');
}
async function makeRequestWithJWT() {
const jwt = localStorage.getItem('jwt')
const response = await fetch(`/protected?jwt=${jwt}`, {method: 'post'});
const result = await response.json();
return result;
}
JSON Body¶
This looks very similar to the Headers approach, except that we send the JWT in as part of the JSON Body instead of a header. Be aware that HEAD or GET requests cannot have a JSON body as part of the request, so this only works for actions like POST/PUT/PATCH/DELETE/etc.
Sending JWTs in a JSON body is probably not very useful most of the time, but we include the option for it regardless.
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');
}
// 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;
}