JWT Authentication in Python: FastAPI and Flask Implementation Guide

Python's rich ecosystem provides multiple robust frameworks for implementing JWT authentication. In this comprehensive guide, we'll explore how to implement secure JWT authentication using two of Python's most popular web frameworks: FastAPI and Flask. We'll cover everything from basic setup to advanced security features, with a focus on real-world applications and best practices.

Why Choose Python for JWT Authentication?

Python offers several advantages for implementing JWT authentication:

  1. Rich Framework Ecosystem: Frameworks like FastAPI and Flask provide built-in support for JWT authentication.
  2. Strong Security Libraries: Python's security libraries are well-maintained and regularly updated.
  3. Type Hints Support: Modern Python's type hints help catch authentication-related bugs early.
  4. Excellent Documentation: Both FastAPI and Flask have comprehensive documentation for security features.

FastAPI Implementation

FastAPI is a modern, fast web framework that's perfect for building authenticated APIs. It provides built-in support for OAuth2 with JWT tokens and automatic API documentation.

Basic Setup

First, let's set up our FastAPI application with the necessary dependencies and models:

python
1from fastapi import FastAPI, Depends, HTTPException, status
2from fastapi.security import OAuth2PasswordBearer
3from jose import JWTError, jwt
4from datetime import datetime, timedelta
5from typing import Optional, Dict, Any
6from pydantic import BaseModel
7
8# Models
9class Token(BaseModel):
10 access_token: str
11 token_type: str
12 expires_in: int
13
14class TokenData(BaseModel):
15 sub: Optional[str] = None
16 exp: Optional[int] = None
17
18class User(BaseModel):
19 username: str
20 email: str
21 disabled: Optional[bool] = None
22
23# Configuration
24SECRET_KEY = "your-secret-key"
25ALGORITHM = "HS256"
26ACCESS_TOKEN_EXPIRE_MINUTES = 30
27
28app = FastAPI()
29oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

This setup provides several important features:

  • Type-safe models using Pydantic
  • Built-in OAuth2 password flow
  • Automatic OpenAPI documentation
  • Configurable token expiration

Token Generation

python
1def create_access_token(data: Dict[str, Any], expires_delta: timedelta) -> str:
2 to_encode = data.copy()
3 expire = datetime.utcnow() + expires_delta
4 to_encode.update({
5 "exp": expire,
6 "iat": datetime.utcnow()
7 })
8 return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
9
10async def get_current_user(token: str = Depends(oauth2_scheme)) -> User:
11 credentials_exception = HTTPException(
12 status_code=status.HTTP_401_UNAUTHORIZED,
13 detail="Could not validate credentials",
14 headers={"WWW-Authenticate": "Bearer"},
15 )
16 try:
17 payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
18 username: str = payload.get("sub")
19 if username is None:
20 raise credentials_exception
21 token_data = TokenData(sub=username)
22 except JWTError:
23 raise credentials_exception
24
25 user = get_user(username=token_data.sub)
26 if user is None:
27 raise credentials_exception
28 return user

Key security considerations in token handling:

  • Use of cryptographically secure algorithms
  • Proper error handling for invalid tokens
  • Automatic token expiration
  • Type-safe token validation

FastAPI Routes

python
1@app.post("/token", response_model=Token)
2async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
3 user = authenticate_user(form_data.username, form_data.password)
4 if not user:
5 raise HTTPException(
6 status_code=status.HTTP_401_UNAUTHORIZED,
7 detail="Incorrect username or password",
8 headers={"WWW-Authenticate": "Bearer"},
9 )
10
11 access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
12 access_token = create_access_token(
13 data={"sub": user.username},
14 expires_delta=access_token_expires
15 )
16
17 return {
18 "access_token": access_token,
19 "token_type": "bearer",
20 "expires_in": ACCESS_TOKEN_EXPIRE_MINUTES * 60
21 }
22
23@app.get("/users/me", response_model=User)
24async def read_users_me(current_user: User = Depends(get_current_user)):
25 return current_user

Flask Implementation

Basic Setup

python
1from flask import Flask, jsonify, request
2from flask_jwt_extended import (
3 JWTManager, create_access_token,
4 jwt_required, get_jwt_identity
5)
6from datetime import timedelta
7
8app = Flask(__name__)
9
10# Setup Flask-JWT-Extended
11app.config["JWT_SECRET_KEY"] = "your-secret-key"
12app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=1)
13jwt = JWTManager(app)

Token Management

python
1class TokenManager:
2 def __init__(self):
3 self.blacklist = set()
4
5 def add_to_blacklist(self, jti):
6 self.blacklist.add(jti)
7
8 def is_blacklisted(self, jti):
9 return jti in self.blacklist
10
11token_manager = TokenManager()
12
13@jwt.token_in_blocklist_loader
14def check_if_token_revoked(jwt_header, jwt_payload):
15 jti = jwt_payload["jti"]
16 return token_manager.is_blacklisted(jti)

Flask Routes

python
1@app.route("/login", methods=["POST"])
2def login():
3 username = request.json.get("username", None)
4 password = request.json.get("password", None)
5
6 user = authenticate_user(username, password)
7 if not user:
8 return jsonify({"error": "Invalid credentials"}), 401
9
10 access_token = create_access_token(
11 identity=username,
12 additional_claims={
13 "roles": user.roles,
14 "email": user.email
15 }
16 )
17
18 return jsonify({
19 "access_token": access_token,
20 "token_type": "bearer",
21 "expires_in": 3600
22 })
23
24@app.route("/protected", methods=["GET"])
25@jwt_required()
26def protected():
27 current_user = get_jwt_identity()
28 return jsonify(logged_in_as=current_user), 200

Security Best Practices

Rate Limiting

python
1from flask_limiter import Limiter
2from flask_limiter.util import get_remote_address
3
4limiter = Limiter(
5 app=app,
6 key_func=get_remote_address,
7 default_limits=["200 per day", "50 per hour"]
8)
9
10@app.route("/login", methods=["POST"])
11@limiter.limit("5 per minute")
12def login():
13 # ... login logic
14 pass

Refresh Token Implementation

python
1class RefreshTokenStore:
2 def __init__(self):
3 self.refresh_tokens = {}
4
5 def store_refresh_token(self, user_id: str, refresh_token: str):
6 self.refresh_tokens[user_id] = refresh_token
7
8 def validate_refresh_token(self, user_id: str, refresh_token: str) -> bool:
9 stored_token = self.refresh_tokens.get(user_id)
10 return stored_token == refresh_token
11
12 def revoke_refresh_token(self, user_id: str):
13 self.refresh_tokens.pop(user_id, None)
14
15@app.route("/refresh", methods=["POST"])
16@jwt_required(refresh=True)
17def refresh():
18 identity = get_jwt_identity()
19 access_token = create_access_token(identity=identity)
20 return jsonify(access_token=access_token)

Error Handling

python
1class AuthError(Exception):
2 def __init__(self, error: str, status_code: int):
3 super().__init__()
4 self.error = error
5 self.status_code = status_code
6
7@app.errorhandler(AuthError)
8def handle_auth_error(error):
9 return jsonify({
10 "error": error.error,
11 "message": "Authentication failed"
12 }), error.status_code
13
14@jwt.expired_token_loader
15def expired_token_callback(jwt_header, jwt_payload):
16 return jsonify({
17 "error": "token_expired",
18 "message": "The token has expired"
19 }), 401

Testing

python
1import pytest
2from fastapi.testclient import TestClient
3
4def test_login_success(client: TestClient):
5 response = client.post(
6 "/token",
7 data={
8 "username": "testuser",
9 "password": "testpass"
10 }
11 )
12 assert response.status_code == 200
13 assert "access_token" in response.json()
14
15def test_protected_route(client: TestClient, valid_token):
16 response = client.get(
17 "/users/me",
18 headers={"Authorization": f"Bearer {valid_token}"}
19 )
20 assert response.status_code == 200

Related Resources

Conclusion

Python's FastAPI and Flask frameworks provide robust solutions for implementing JWT authentication. By following these patterns and security practices, you can build secure and scalable authentication systems.

Remember to check our other security guides and authentication tools for more resources!

text
1This guide provides comprehensive coverage of JWT implementation in Python, focusing on both FastAPI and Flask frameworks.

Suggested Articles