flask_jwt - dwilson2547/wiki_demo GitHub Wiki
Flask-JWT (or more commonly, Flask-JWT-Extended) is an extension for Flask that provides JSON Web Token (JWT) authentication for securing APIs. JWTs are a stateless, compact, and URL-safe way to authenticate users and transmit information between parties as a JSON object. Flask-JWT-Extended simplifies the process of generating, validating, and managing JWTs in Flask applications.
- 1. What is JWT?
- 2. Why Use Flask-JWT-Extended?
- 3. Key Features of Flask-JWT-Extended
- 4. Installation
- 5. Basic Setup
- 6. Advanced Features
- 7. Flask-JWT-Extended vs. Flask-JWT
- 8. Strengths of Flask-JWT-Extended
- 9. Weaknesses of Flask-JWT-Extended
- 10. When to Use Flask-JWT-Extended
- 11. Example: Full Authentication Flow
- 12. Learning Resources
- 13. Summary
JSON Web Token (JWT) is a standard (RFC 7519) for securely transmitting information between parties as a JSON object. It consists of three parts:
-
Header: Contains metadata (e.g., token type, algorithm).
{ "alg": "HS256", "typ": "JWT" }
-
Payload: Contains claims (e.g., user ID, expiration time).
{ "sub": "1234567890", "name": "Alice", "iat": 1516239022 }
- Signature: Ensures the token hasnβt been tampered with (generated using a secret key).
Example JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- Stateless Authentication: No server-side session storage required.
- Secure: Tokens are signed and can be verified.
- Flexible: Supports custom claims and token expiration.
- Easy Integration: Works seamlessly with Flask routes.
- Extensible: Supports refresh tokens, blacklisting, and more.
- Generate Tokens: Create access and refresh tokens.
- Validate Tokens: Automatically verify tokens in protected routes.
- Use decorators to restrict access to authenticated users.
from flask_jwt_extended import jwt_required @app.route('/protected', methods=['GET']) @jwt_required() def protected(): return {"message": "This is a protected route!"}
- Add custom data to JWT payloads.
from flask_jwt_extended import create_access_token access_token = create_access_token(identity=user_id, additional_claims={"role": "admin"})
- Issue short-lived access tokens and long-lived refresh tokens for improved security.
from flask_jwt_extended import create_refresh_token refresh_token = create_refresh_token(identity=user_id)
-
Revoke tokens before they expire (e.g., on logout).
from flask_jwt_extended import get_jti from flask_jwt_extended.token_store import TokenStore @app.route('/logout', methods=['DELETE']) @jwt_required() def logout(): jti = get_jti(current_token) token_store = TokenStore() token_store.add(jti) # Blacklist the token return {"message": "Successfully logged out"}
- Tokens are typically sent in the Authorization header:
Authorization: Bearer <your_token_here>
pip install flask-jwt-extended
from flask import Flask
from flask_jwt_extended import JWTManager
app = Flask(__name__)
# Configure JWT
app.config["JWT_SECRET_KEY"] = "your-secret-key" # Change this!
jwt = JWTManager(app)
from flask import request, jsonify
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
# Mock user database
users = {
"alice": {"password": "secret", "role": "admin"},
"bob": {"password": "password", "role": "user"}
}
@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
user = users.get(username, None)
if not user or user["password"] != password:
return jsonify({"msg": "Bad username or password"}), 401
# Create access token
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token)
@app.route('/protected', methods=['GET'])
@jwt_required()
def protected():
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
flask run
-
Login:
curl -X POST http://127.0.0.1:5000/login \ -H "Content-Type: application/json" \ -d '{"username": "alice", "password": "secret"}'
Response:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
-
Access Protected Route:
curl http://127.0.0.1:5000/protected \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response:
{ "logged_in_as": "alice" }
-
Modify Token Creation/Validation:
@jwt.token_in_blocklist_loader def check_if_token_revoked(jwt_header, jwt_payload): jti = jwt_payload["jti"] return jti in token_blocklist # Check if token is blacklisted @jwt.user_identity_loader def user_identity_lookup(user): return user.id # Use user ID as identity
-
Require Fresh Tokens for sensitive operations:
@app.route('/admin', methods=['GET']) @jwt_required(fresh=True) def admin(): return jsonify({"message": "Admin access granted"})
-
Store Tokens in HTTP-Only Cookies for security:
app.config["JWT_TOKEN_LOCATION"] = ["cookies"] app.config["JWT_COOKIE_SECURE"] = True # HTTPS only app.config["JWT_COOKIE_CSRF_PROTECT"] = True # Enable CSRF protection
-
Blacklist Tokens on logout:
from flask_jwt_extended import get_jti token_blocklist = set() @app.route('/logout', methods=['DELETE']) @jwt_required() def logout(): jti = get_jti(current_token) token_blocklist.add(jti) return jsonify({"message": "Successfully logged out"})
Feature | Flask-JWT-Extended | Flask-JWT (Deprecated) |
---|---|---|
Maintenance | Actively maintained | Deprecated (use Flask-JWT-Extended) |
Refresh Tokens | β Built-in support | β No built-in support |
Token Blacklisting | β Easy to implement | β Limited support |
Custom Claims | β Full support | β Limited support |
CSRF Protection | β Built-in | β No built-in support |
Documentation | β Comprehensive | β Outdated |
β Easy to Use: Simple setup and integration with Flask. β Stateless: No server-side session storage required. β Secure: Supports token signing, expiration, and blacklisting. β Flexible: Customize token creation, validation, and claims. β Refresh Tokens: Issue short-lived access tokens and long-lived refresh tokens. β Extensible: Add custom logic for token handling.
β Token Storage: Requires a database or cache (e.g., Redis) for blacklisting tokens. β Complexity: Managing token expiration and refresh logic can be tricky. β Performance: Token validation adds overhead to each request.
- API Authentication: Secure REST APIs with JWT.
- Stateless Apps: When you donβt want to manage server-side sessions.
- Microservices: Authenticate requests between services.
- Single-Page Applications (SPAs): Frontend apps (React, Vue) can store tokens and make authenticated requests.
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
with app.app_context():
db.create_all()
from werkzeug.security import generate_password_hash, check_password_hash
@app.route('/register', methods=['POST'])
def register():
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 User.query.filter_by(username=username).first():
return jsonify({"msg": "Username already exists"}), 400
hashed_password = generate_password_hash(password)
user = User(username=username, password=hashed_password)
db.session.add(user)
db.session.commit()
return jsonify({"msg": "User created successfully"}), 201
@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).first()
if not user or not check_password_hash(user.password, password):
return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(identity=user.id)
return jsonify(access_token=access_token, refresh_token=refresh_token), 200
@app.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh():
current_user = get_jwt_identity()
new_token = create_access_token(identity=current_user)
return jsonify(access_token=new_token), 200
@app.route('/admin', methods=['GET'])
@jwt_required()
def admin():
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user.username != "admin":
return jsonify({"msg": "Admin access required"}), 403
return jsonify({"message": "Welcome, admin!"}), 200
- Official Documentation: flask-jwt-extended.readthedocs.io
- Tutorials:
- GitHub: vimalloc/flask-jwt-extended
- Flask-JWT-Extended is a powerful, flexible extension for adding JWT authentication to Flask applications.
- Key Features: Token creation/validation, protected routes, refresh tokens, and custom claims.
- Strengths: Stateless, secure, and easy to integrate with Flask.
- Weaknesses: Requires token storage for blacklisting and can add complexity.
- Use Cases: Ideal for securing APIs, microservices, and SPAs where stateless authentication is desired.
Flask-JWT-Extended is the go-to choice for Flask developers who need a robust, modern authentication system without the overhead of session management.