Skip to main content

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:

  1. The server responds with a 401 Unauthorized status code and a WWW-Authenticate header.
  2. The client sends the credentials (username and password) encoded in base64 format in the Authorization header.
  3. 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:

bash
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:

python
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

  1. We import the necessary components from FastAPI, including HTTPBasic and HTTPBasicCredentials from fastapi.security.
  2. We create an instance of HTTPBasic() called security.
  3. 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
  4. 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:

  1. Open your browser and navigate to http://localhost:8000/protected
  2. The browser will show a login prompt
  3. Enter the username (admin) and password (secretpassword)
  4. If correct, you'll see the protected content

Using curl

You can also test using curl with the -u flag:

bash
# 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:

python
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:

python
@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:

  1. Always use HTTPS: Basic auth sends credentials encoded (but not encrypted), so HTTPS is essential for security.

  2. Use password hashing: Never store plain text passwords. Use libraries like passlib to hash passwords.

  3. Consider rate limiting: Implement rate limiting to prevent brute force attacks on your authentication endpoints.

  4. Use secure string comparison: Always use secrets.compare_digest() for comparing security-sensitive strings to prevent timing attacks.

  5. 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:

python
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

Exercise

Create a FastAPI application with:

  1. A public endpoint that returns a welcome message
  2. A protected endpoint that requires basic authentication
  3. User credentials stored in a dictionary (simulating a database)
  4. Password hashing using passlib
  5. 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! :)