Building secure APIs is essential. Whether you’re protecting user data, securing business logic, or managing access to premium features, authentication forms the backbone of your application’s security.
The challenge? Authentication often resembles a complex maze of tokens, sessions, headers, and security protocols. When you add the pressure of selecting the most appropriate approach for your specific use case, it’s easy to feel overwhelmed.
Here’s the good news : FastAPI simplifies authentication, making it both powerful and user-friendly. With its built-in security features, automatic documentation generation, and Python’s straightforward syntax, you can implement strong authentication without the hassle.
In this guide, we’ll walk through three core ways to authenticate users:
- Basic HTTP authentication : Ideal for internal APIs, microservices, and rapid prototyping
- API key authentication : Preferred for public APIs, third-party integrations, and automated systems.
- Session-based authentication : Ideal for traditional web applications and user-facing interfaces.
By the end, you’ll understand not just how to implement each method, but when to use them, along with the security best practices.
Authentication vs Authorization
These two often get mixed up. Here’s the quick breakdown:
- Authentication is like checking someone’s ID at the door. It gives an answer to the question: “Who are you?”. When a user logs in with their username and password, authentication happens.
- Authorization is like checking if that person has VIP access. It gives an answer to the question: “What are you allowed to do?”. Once you know who someone is, you decide if they can access admin features, delete posts or even give access to other people.
Think of it this way: authentication gets you into the building, authorization determines which rooms you can enter. FastAPI helps with both, but we’re focusing on authentication in this article.
Setting Up
Let’s start with the basic project structure and dependencies:
Create a new project directory:
mkdir fastapi-auth-demo && cd fastapi-auth-demo
Set up the virtual environment:
# Create virtual environment
python -m venv venv
# Activate it
./venv/Scripts/activate # Windows
source venv/bin/activate # macOS/Linux
Create a requirements.txt and add these dependencies:
fastapi==0.104.1
uvicorn[standard]==0.24.0
python-multipart==0.0.6
passlib[bcrypt]==1.7.4
python-jose[cryptography]==3.3.0
itsdangerous==2.2.0
Install the dependencies:
pip install -r ./requirements.txt
Now let’s create our main FastAPI app with some basic setup:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(title="FastAPI Authentication Demo", version="1.0.0")
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"], )
Basic HTTP Authentication
Basic HTTP Authentication is the simplest form of authentication, where credentials (username and password) are sent with each request. This type of authentication is most suitable for internal API or quick prototypes.
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
security = HTTPBasic()
fake_users_db = {
"john": {"username": "john", "password": "secret123", "role": "user"},
"admin": {"username": "admin", "password": "supersecret", "role": "admin"}
}
def authenticate_user(credentials: HTTPBasicCredentials = Depends(security)):
user = fake_users_db.get(credentials.username)
if not user or not secrets.compare_digest(credentials.password, user["password"]):
raise HTTPException(status_code=401, detail="Invalid credentials")
return user
@app.get("/basic/profile")
def read_profile(user: dict = Depends(authenticate_user)):
return {"message": f"Welcome {user['username']}!"}
@app.get("/basic/admin")
def admin_area(user: dict = Depends(authenticate_user)):
if user["role"] != "admin":
raise HTTPException(status_code=403, detail="Admins only")
return {"message": "You're in the admin zone."}
API Key Authentication
API key authentication is popular for API access, where clients include a key in headers or query parameters. Instead of sending a username and password for every request, clients send a special key that identifies them.
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
api_key_header = HTTPBearer()
fake_api_keys = {
"sk_test_123": {"key_id": "1", "permissions": ["read", "write"]},
"sk_live_456": {"key_id": "2", "permissions": ["read"]}
}
def validate_api_key(credentials: HTTPAuthorizationCredentials = Depends(api_key_header)):
key = credentials.credentials
if key not in fake_api_keys:
raise HTTPException(status_code=401, detail="Invalid API Key")
return fake_api_keys[key]
@app.get("/apikey/data")
def read_data(api_key=Depends(validate_api_key)):
return {"data": ["item1", "item2"]}
@app.post("/apikey/create")
def create_item(item: dict, api_key=Depends(validate_api_key)):
if "write" not in api_key["permissions"]:
raise HTTPException(status_code=403, detail="Write access required")
return {"message": "Item created", "item": item}
Session-Based Authentication
Session-based authentication stores user state on the server and uses session cookies for subsequent requests. When you log in, the server creates a session and gives you a cookie. It’s like getting a wristband at a concert — as long as you have it, you can move around freely.
from fastapi import FastAPI, Request, Form, Depends, HTTPException, status
from fastapi.responses import JSONResponse
from starlette.middleware.sessions import SessionMiddleware
from pydantic import BaseModel
# Add session middleware
app.add_middleware(SessionMiddleware, secret_key="super-secret-session-key")
# Mock user database
users_db = {
"alice": "password123",
"bob": "securepass"
}
class LoginSchema(BaseModel):
username: str
password: str
@app.post("/login")
async def login(request: Request, payload: LoginSchema):
username = payload.username
password = payload.password
if username in users_db and users_db[username] == password:
request.session["user"] = username
return JSONResponse({"message": "Login successful"})
raise HTTPException(status_code=401, detail="Invalid credentials")
@app.get("/dashboard")
async def dashboard(request: Request):
user = request.session.get("user")
if not user:
raise HTTPException(status_code=401, detail="Not authenticated")
return {"message": f"Welcome to your dashboard, {user}!"}
@app.post("/logout")
async def logout(request: Request):
request.session.clear()
return {"message": "Logged out successfully"}
Error Handling
This article dives deeper in various HTTP responses and their error handling techniques. Consistent error handling improves security and user experience:
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
"""Custom HTTP exception handler"""
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"message": exc.detail,
"type": "authentication_error" if exc.status_code == 401 else "authorization_error",
"status_code": exc.status_code
}
},
headers=exc.headers
)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
"""Handle validation errors"""
return JSONResponse(
status_code=422,
content={
"error": {
"message": "Validation error",
"type": "validation_error",
"details": exc.errors()
}
}
)
Security Best Practices
1. Never store plain text passwords, always hash password using bcrypt:
from passlib.context import CryptContext
# Use proper password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str) -> str:
return pwd_context.hash(password)
2. Rate Limiting
from collections import defaultdict
import time
# Simple rate limiting (use Redis in production)
request_counts = defaultdict(list)
def rate_limit(max_requests: int = 100, window_minutes: int = 15):
def decorator(func):
def wrapper(*args, **kwargs):
client_ip = "127.0.0.1"
now = time.time()
window_start = now - (window_minutes * 60)
# Clean old requests
request_counts[client_ip] = [
req_time for req_time in request_counts[client_ip]
if req_time > window_start
]
if len(request_counts[client_ip]) >= max_requests:
raise HTTPException(
status_code=429,
detail="Rate limit exceeded"
)
request_counts[client_ip].append(now)
return func(*args, **kwargs)
return wrapper
return decorator
3. Use environment variables for secrets, with pydantic settings. Never hard code secret keys in your code:
# config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
secret_key: str = "your-secret-key-here"
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
class Config:
env_file = ".env"
settings = Settings()
Wrapping Up
We’ve covered three authentication strategies in FastAPI:
- Basic HTTP Authentication : Simple but suitable for internal APIs.
- API Key Authentication : Great for public APIs and service-to-service communication.
- Session-Based Authentication : Traditional and cookie based approach.
Each method has its use cases, and the choice depends on your application’s requirements.
Key Takeaways:
- Always use HTTPS in production.
- Hash passwords properly with bcrypt or similar.
- Implement proper error handling and logging.
- Add security headers and middleware.
- Test your authentication thoroughly.
- Add rate limiting for public APIs.
- Use environment variables to store sensitive data
Dig Deeper
- How to secure APIs
- Authentication and Authorization with FastAPI
- Security Best Practices
- Simple HTTP Authentication
Have a great one!!!
Author: Jane Nkwor
Thank you for being a part of the community
Before you go:
Whenever you’re ready
There are 4 ways we can help you become a great backend engineer:
- The MB Platform: Join thousands of backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.
- The MB Academy: The “MB Academy” is a 6-month intensive Advanced Backend Engineering Boot Camp to produce great backend engineers.
- Join Backend Weekly: If you like posts like this, you will absolutely enjoy our exclusive weekly newsletter, sharing exclusive backend engineering resources to help you become a great Backend Engineer.
- Get Backend Jobs: Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board.
Originally published at https://masteringbackend.com on September 2, 2025.
Top comments (0)