FastAPI HTTP Basic Auth
Introduction
HTTP Basic Authentication is one of the simplest authentication mechanisms available for securing web applications and APIs. Despite its simplicity, it remains useful for many scenarios, especially for internal tools or APIs that require a straightforward authentication layer.
In this guide, we'll explore how to implement HTTP Basic Authentication in FastAPI applications. This authentication method requires users to provide a username and password to access protected resources, with credentials transmitted in the HTTP header.
What is HTTP Basic Authentication?
HTTP Basic Authentication is a simple authentication scheme built into the HTTP protocol. When a client makes a request to a protected resource:
- The server responds with a
401 Unauthorized
status code and aWWW-Authenticate
header. - The client sends the credentials (username and password) encoded in base64 format in the
Authorization
header. - The server validates these credentials and either grants or denies access.
While the credentials are encoded, they are not encrypted, which is why Basic Authentication should always be used over HTTPS connections.
Implementing Basic Auth in FastAPI
FastAPI provides built-in support for Basic Authentication through its security utilities. Let's see how to implement it:
Prerequisites
First, ensure you have FastAPI and its dependencies installed:
pip install fastapi uvicorn
pip install python-multipart # Required for security dependencies
Basic Implementation
Here's a simple implementation of HTTP Basic Authentication in FastAPI:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
app = FastAPI()
security = HTTPBasic()
# Hardcoded credentials (in a real app, you'd use a database)
USERNAME = "admin"
PASSWORD = "secretpassword"
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
correct_username = secrets.compare_digest(credentials.username, USERNAME)
correct_password = secrets.compare_digest(credentials.password, PASSWORD)
if not (correct_username and correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/")
def read_root():
return {"message": "Public endpoint"}
@app.get("/protected")
def read_protected_data(username: str = Depends(get_current_username)):
return {"message": f"Hello, {username}! This is protected data."}
How It Works
- We import the necessary components from FastAPI, including
HTTPBasic
andHTTPBasicCredentials
fromfastapi.security
. - We create an instance of
HTTPBasic()
calledsecurity
. - We define a dependency function
get_current_username()
that:- Takes the credentials provided by the client using
Depends(security)
- Validates the credentials against our expected values
- Uses
secrets.compare_digest()
to prevent timing attacks when comparing strings - Raises an
HTTPException
if the credentials are invalid - Returns the username if the credentials are valid
- Takes the credentials provided by the client using
- We apply this dependency to the
/protected
endpoint, which only allows access when valid credentials are provided.
Testing the Authentication
You can test the authentication using different methods:
Using a Web Browser
When you access the /protected
route in a browser, it will show a login dialog:
- Open your browser and navigate to
http://localhost:8000/protected
- The browser will show a login prompt
- Enter the username (
admin
) and password (secretpassword
) - If correct, you'll see the protected content
Using curl
You can also test using curl with the -u
flag:
# Access public endpoint (no auth required)
curl http://localhost:8000/
# Access protected endpoint with credentials
curl -u admin:secretpassword http://localhost:8000/protected
# Access with incorrect credentials
curl -u wrong:password http://localhost:8000/protected
Advanced Usage
Using a Database for Authentication
In real-world applications, you'd want to store user credentials in a database rather than hardcoding them. Here's how you might extend the example:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
from passlib.context import CryptContext
app = FastAPI()
security = HTTPBasic()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Simulated database of users (replace with actual database in real apps)
users_db = {
"alice": {
"username": "alice",
"hashed_password": pwd_context.hash("wonderland"),
},
"bob": {
"username": "bob",
"hashed_password": pwd_context.hash("builder"),
},
}
def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
username = credentials.username
if username not in users_db:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid username",
headers={"WWW-Authenticate": "Basic"},
)
user = users_db[username]
if not pwd_context.verify(credentials.password, user["hashed_password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid password",
headers={"WWW-Authenticate": "Basic"},
)
return user
@app.get("/users/me")
def read_current_user(user = Depends(get_current_user)):
return {"username": user["username"]}
Multiple Protected Endpoints
You can protect multiple endpoints using the same authentication dependency:
@app.get("/admin/dashboard")
def admin_dashboard(user = Depends(get_current_user)):
return {"message": "Welcome to admin dashboard"}
@app.get("/admin/settings")
def admin_settings(user = Depends(get_current_user)):
return {"message": "Admin settings page"}
Best Practices
When using HTTP Basic Authentication in FastAPI, keep these best practices in mind:
-
Always use HTTPS: Basic auth sends credentials encoded (but not encrypted), so HTTPS is essential for security.
-
Use password hashing: Never store plain text passwords. Use libraries like
passlib
to hash passwords. -
Consider rate limiting: Implement rate limiting to prevent brute force attacks on your authentication endpoints.
-
Use secure string comparison: Always use
secrets.compare_digest()
for comparing security-sensitive strings to prevent timing attacks. -
Consider alternatives for public APIs: For public-facing APIs, consider more robust authentication mechanisms like OAuth2 or JWT.
Common Issues and Solutions
Issue: Authentication Popup Keeps Appearing
Problem: The browser keeps showing the authentication popup even after entering correct credentials.
Solution: Ensure you're returning the username after successful authentication and not raising any exceptions.
Issue: No Authentication Prompt in Browser
Problem: The browser doesn't display an authentication prompt when accessing protected routes.
Solution: Ensure your exception includes the correct headers:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Basic"},
)
Summary
HTTP Basic Authentication provides a simple way to secure FastAPI endpoints with username and password protection. While not suitable for all scenarios due to its limitations, it's perfect for internal tools, development environments, and simple APIs where you need a straightforward authentication method.
In this guide, we've learned:
- The fundamentals of HTTP Basic Authentication
- How to implement it in FastAPI using built-in security utilities
- How to validate credentials securely
- Best practices for real-world implementation
- Common issues and their solutions
Remember that for more complex applications or public-facing APIs, you might want to consider more advanced authentication methods like OAuth2 or JWT, which FastAPI also supports excellently.
Further Resources
- FastAPI Official Documentation on Security
- MDN Web Docs: HTTP Authentication
- OWASP Authentication Cheat Sheet
Exercise
Create a FastAPI application with:
- A public endpoint that returns a welcome message
- A protected endpoint that requires basic authentication
- User credentials stored in a dictionary (simulating a database)
- Password hashing using
passlib
- A way to "register" new users (another endpoint that adds entries to your user dictionary)
This exercise will help you consolidate your understanding of HTTP Basic Authentication in FastAPI.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)