Authentication Middleware — Reusable Auth Logic for API Frameworks
In this tutorial, you will learn about Authentication Middleware. We cover key concepts, practical examples, and best practices to help you master this topic.
Authentication middleware is a reusable software component that intercepts incoming API requests, validates credentials, and either attaches user context or rejects the request.
What You'll Learn
How to build authentication middleware for different frameworks, handle multiple auth methods, attach user context to requests, and return proper error responses.
Why It Matters
Without middleware, every endpoint repeats authentication logic. This leads to inconsistencies, missing validation on some routes, and difficult maintenance. Middleware centralizes authentication so one change updates all protected endpoints.
Real-World Use
Flask @login_required, Express passport.authenticate(), and Django REST Framework's AuthenticationClasses are all middleware that handle authentication before requests reach route handlers.
flowchart LR
A["Request"] --> B["Auth Middleware"]
B --> C{"Has valid auth?"}
C -->|"Yes — attach user"| D["Route Handler"]
C -->|"No"| E["401 Response"]
D --> F["Response"]
style A fill:#dbeafe,stroke:#2563eb
style B fill:#fef3c7,stroke:#d97706
style D fill:#dcfce7,stroke:#16a34a
style E fill:#fecaca,stroke:#dc2626
Code Example: Flask Auth Middleware Decorator
from flask import Flask, request, jsonify, g
from functools import wraps
import jwt
app = Flask(__name__)
SECRET = "your-secret"
def require_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return jsonify({
"error": "Unauthorized",
"message": "Missing or invalid Authorization header"
}), 401
token = auth_header[7:]
try:
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
g.current_user = payload # Attach to request context
except jwt.ExpiredSignatureError:
return jsonify({
"error": "Token expired",
"message": "Please refresh your token"
}), 401
except jwt.InvalidTokenError as e:
return jsonify({
"error": "Invalid token",
"detail": str(e)
}), 401
return f(*args, **kwargs)
return decorated
# Usage
@app.route("/api/profile")
@require_auth
def profile():
return jsonify({
"user": g.current_user["sub"],
"role": g.current_user.get("role", "user")
})
@app.route("/api/public")
def public():
return jsonify({"message": "This is public"})
Code Example: Express.js Auth Middleware
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Missing or invalid Authorization header'
});
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, SECRET);
req.user = decoded;
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired' });
}
return res.status(401).json({ error: 'Invalid token' });
}
}
// Usage
app.get('/api/profile', authenticate, (req, res) => {
res.json({ user: req.user.sub, role: req.user.role });
});
Code Example: FastAPI Auth Middleware
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
app = FastAPI()
security = HTTPBearer(auto_error=False)
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security)
):
if not credentials:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing authorization"
)
try:
payload = jwt.decode(
credentials.credentials, SECRET, algorithms=["HS256"]
)
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
@app.get("/api/profile")
async def profile(user: dict = Depends(get_current_user)):
return {"user": user["sub"], "role": user.get("role")}
@app.get("/api/public")
async def public():
return {"message": "Public endpoint"}
Common Mistakes
1. Not Using @wraps (Flask) or functools.wraps
Without wraps, the decorated function loses its metadata (name, docstring). This breaks route registration and introspection.
2. Silent Authentication Failures
Returning 500 instead of 401 makes debugging impossible. Always return proper HTTP status codes.
3. Not Handling All JWT Error Types
ExpiredSignatureError and InvalidTokenError are different. Return different error messages so clients know whether to refresh or re-login.
4. Leaking Token Data in Error Messages
Including the full token in error messages exposes credentials. Truncate or hash token values in logs and errors.
5. Applying Middleware to Public Routes
Public routes should bypass authentication. Use route-level decorators or exclude paths in the middleware configuration.
Practice Questions
- What is the purpose of authentication middleware?
- How does
gwork in Flask middleware? - Why should middleware return 401 instead of 403 for missing auth?
- How does Express.js pass user data from middleware to route handlers?
- What is the difference between middleware and route-level auth?
Answers:
- Middleware intercepts requests before route handlers, validates authentication, and attaches user context — centralizing auth logic.
- Flask's
gobject stores data for the current request. Middleware attaches user info tog, and route handlers access it viag.current_user. - 401 means "authentication required or failed." 403 means "authenticated but not authorized." Use 401 for missing/invalid credentials.
- By attaching data to the
reqobject (req.user = decoded). Route handlers access it viareq.user. - Middleware applies to all routes or a group. Route-level auth applies to specific endpoints. Use middleware for general auth, route-level for permissions.
Challenge: Build a Flask auth middleware that supports multiple auth methods (Bearer JWT, Basic Auth, API Key) and attaches the appropriate user context. Include proper error responses for each failure case.
FAQ
Mini Project
Build a Flask application that uses authentication middleware to protect admin and user routes. Include a public health endpoint, user endpoints (Bearer JWT required), and admin endpoints (JWT with admin role required). Test each with curl.
What's Next
Now complete the API Authentication Project — a capstone that combines everything you have learned into a working authentication system.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro