Stateless JWT Authentication with Flask-JWT-Extended0%
Background Tasks with Celery and Redis

Stateless JWT Authentication with Flask-JWT-Extended

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Stateless JWT Authentication with Flask-JWT-Extended

While cookie-based sessions (like Flask-Login) work seamlessly for traditional server-rendered websites, modern mobile apps and Single Page Applications (Next.js, React) require stateless authentication. Storing session IDs in cookies across cross-domain mobile applications triggers CORS restrictions and cross-site cookie blocking.

JSON Web Tokens (JWT) provide cryptographically signed, stateless authentication tokens. The Flask-JWT-Extended extension is the premier library for implementing JWT authentication in Flask.


1. How JWT Authentication Works

A JWT is an encoded, cryptographically signed token containing three base64-encoded segments:

Output
[ Header (algorithm & token type) ].[ Payload (claims & user ID) ].[ Signature (HMAC-SHA256) ]
Output
1. Client sends POST /api/login with { username, password }.
2. Server verifies password and generates two tokens:
- Access Token: Short-lived (e.g. 15 minutes), used to access protected APIs.
- Refresh Token: Long-lived (e.g. 30 days), used only to request a fresh access token.
3. Client stores tokens and attaches:
Authorization: Bearer <access_token> on subsequent API requests.
4. Server cryptographically verifies signature without querying session tables!

2. Installing and Configuring Flask-JWT-Extended

Bash / Terminal
pip install flask-jwt-extended

In app.py:

Python
import os
from datetime import timedelta
from flask import Flask
from flask_jwt_extended import JWTManager
 
app = Flask(__name__)
 
# Essential: Strong secret key used to sign JWT signatures
app.config["JWT_SECRET_KEY"] = os.environ.get("JWT_SECRET_KEY", "jwt-super-secret-key-prod")
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(minutes=15)
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=30)
 
jwt = JWTManager(app)

3. Login Endpoint & Issuing Tokens

Python
from flask import request, jsonify
from flask_jwt_extended import create_access_token, create_refresh_token
from models import User
 
@app.route("/api/v1/auth/login", methods=["POST"])
def api_login():
data = request.get_json() or {}
username = data.get("username")
password = data.get("password")
 
user = User.query.filter_by(username=username).first()
if not user or not user.verify_password(password):
return jsonify({"error": "Invalid credentials"}), 401
 
# Identity can be user ID or username
# Additional claims store roles or permissions directly in token!
access_token = create_access_token(
identity=str(user.id),
additional_claims={"role": user.role}
)
refresh_token = create_refresh_token(identity=str(user.id))
 
return jsonify({
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "Bearer"
}), 200

4. Protecting Endpoints with @jwt_required()

Restricting an endpoint requires decorating the view with @jwt_required():

Python
from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt
 
@app.route("/api/v1/user/profile", methods=["GET"])
@jwt_required()
def get_user_profile():
# Extracts the user ID encoded in the verified JWT
user_id = get_jwt_identity()
user = User.query.get_or_404(int(user_id))
# Access custom claims
claims = get_jwt()
user_role = claims.get("role")
 
return jsonify({
"id": user.id,
"username": user.username,
"role": user_role
}), 200

5. Refreshing Access Tokens

When an access token expires (401 Token has expired), clients submit their refresh token to obtain a fresh access token without re-prompting for credentials:

Python
@app.route("/api/v1/auth/refresh", methods=["POST"])
@jwt_required(refresh=True)
def refresh_token():
# Only accepts a valid Refresh Token
current_user_id = get_jwt_identity()
new_access_token = create_access_token(identity=current_user_id)
return jsonify({"access_token": new_access_token}), 200

Interactive Quiz

Practice Quiz

Question 1
In which HTTP header does a client typically transmit a JWT token?
Question 2
What is the primary purpose of separating tokens into an Access Token and a Refresh Token?
Question 3
What decorator protects endpoints requiring a valid access token in Flask-JWT-Extended?
Question 4
How do you extract the user identity encoded in the verified JWT inside a route?
Question 5
What setting must be passed to @jwt_required() on an endpoint designed specifically to issue new access tokens?
Next Lesson

Background Tasks with Celery and Redis

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext