flask-jwt-extended is a powerful Flask extension that provides a flexible way to add JWT (JSON Web Token) based authentication to your Flask applications. It simplifies the process of creating, managing, and verifying JWTs, making it easier to secure your API endpoints.
What are JWTs?
JWTs are an open, industry-standard RFC 7519 method for representing claims securely between two parties. They are commonly used for authorization where the client sends a token with each request, and the server verifies it to ensure the client is authenticated and authorized to access the requested resource. JWTs are stateless, meaning the server doesn't need to store session information, which can improve scalability.
Key Features of flask-jwt-extended:
1. Access and Refresh Tokens: Supports the common pattern of using short-lived access tokens for securing resources and long-lived refresh tokens for acquiring new access tokens without re-authenticating.
2. Decorators for Endpoint Protection: Provides easy-to-use decorators like `@jwt_required()`, `@fresh_jwt_required()`, and `@jwt_refresh_token_required()` to protect routes based on the token type.
3. Token Creation and Verification: Functions to programmatically create access and refresh tokens (`create_access_token`, `create_refresh_token`) and handle their verification automatically through decorators.
4. Custom Claims: Allows embedding custom data (claims) into your JWTs, which can be retrieved later to access user-specific information.
5. Token Blacklisting: Provides a mechanism to invalidate tokens (e.g., on logout or password change) by storing their JTI (JWT ID) in a blacklist, preventing them from being used for authentication.
6. Cookies and Headers Support: Can send and receive JWTs via HTTP Only cookies (recommended for browser-based applications) or through the `Authorization` header.
7. Identity Management: Easily retrieve the identity of the current user from a valid JWT using `get_jwt_identity()`.
8. Configuration Options: Extensive configuration options for secret keys, token lifetimes, token locations, and more.
How it Works (Typical Flow):
1. User Login: A user sends credentials to a login endpoint.
2. Token Creation: If credentials are valid, the server creates an access token and a refresh token, signing them with a secret key.
3. Token Return: These tokens are sent back to the client, usually in the response body or as HTTP Only cookies.
4. Protected Resource Access: For subsequent requests to protected routes, the client includes the access token (e.g., in the `Authorization` header).
5. Token Verification: The `flask-jwt-extended` decorator on the server-side verifies the access token's signature, expiration, and ensures it's not blacklisted.
6. Refresh Token Usage: When an access token expires, the client uses the refresh token to request a new access token from a refresh endpoint.
7. Logout: Upon logout, the server adds the access and/or refresh token's JTI to a blacklist, rendering them unusable.
This library is ideal for building secure APIs with Flask, providing a robust and flexible authentication solution that decouples token management from session management.
Example Code
from flask import Flask, jsonify, request
from flask_jwt_extended import (
JWTManager, jwt_required, create_access_token,
create_refresh_token, get_jwt_identity, jwt_refresh_token_required,
set_access_cookies, set_refresh_cookies, unset_jwt_cookies, get_jwt
)
app = Flask(__name__)
Setup the Flask-JWT-Extended extension
app.config["JWT_SECRET_KEY"] = "super-secret-key-that-should-be-in-env"
app.config["JWT_TOKEN_LOCATION"] = ["cookies"]
app.config["JWT_COOKIE_SECURE"] = False Set to True in production (HTTPS)
app.config["JWT_COOKIE_CSRF_PROTECT"] = True
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = 3600 1 hour
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = 2592000 30 days
jwt = JWTManager(app)
A simple set to store blacklisted JWT JTIs
In a real application, this would be a persistent storage like Redis or a database
blacklist = set()
Callback function to check if a JWT has been blacklisted
@jwt.token_in_blocklist_loader
def check_if_token_in_blocklist(jwt_header, jwt_payload):
jti = jwt_payload["jti"]
return jti in blacklist
A simple 'user' database for demonstration
users = {
"testuser": {"password": "testpass"}
}
@app.route('/login', methods=['POST'])
def login():
username = request.json.get('username', None)
password = request.json.get('password', None)
if not username or not password:
return jsonify({"msg": "Missing username or password"}), 400
if username not in users or users[username]['password'] != password:
return jsonify({"msg": "Bad username or password"}), 401
Create the tokens
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
Set the JWTs in http-only cookies
response = jsonify({"msg": "Login successful"})
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
return response, 200
@app.route('/protected', methods=['GET'])
@jwt_required()
def protected():
Access the identity of the current user with get_jwt_identity
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
@app.route('/refresh', methods=['POST'])
@jwt_refresh_token_required()
def refresh():
Create a new access token from the refresh token
current_user = get_jwt_identity()
new_access_token = create_access_token(identity=current_user)
Set the new access token in a http-only cookie
response = jsonify({"msg": "Token refreshed"})
set_access_cookies(response, new_access_token)
return response, 200
@app.route('/logout', methods=['POST'])
@jwt_required()
def logout():
Retrieve the JTI (JWT ID) from the access token to blacklist it
jti = get_jwt()["jti"]
blacklist.add(jti)
response = jsonify({"msg": "Successfully logged out"})
unset_jwt_cookies(response)
return response, 200
@app.route('/logout_refresh', methods=['POST'])
@jwt_refresh_token_required()
def logout_refresh():
Blacklist the refresh token
jti = get_jwt()["jti"]
blacklist.add(jti)
response = jsonify({"msg": "Successfully logged out (refresh token)"})
unset_jwt_cookies(response)
return response, 200
if __name__ == '__main__':
app.run(debug=True)








flask-jwt-extended