Security and Authentication
Learn to implement robust security and authentication mechanisms to protect your FastAPI applications.
Content
Basic Authentication
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Basic Authentication in FastAPI — The Bare‑Bones, Not‑For‑Everything Approach
"Basic" Authentication: simple, honest, and danger‑adjacent. Kind of like showing up to a hacking contest wearing a name tag that says
admin.
You're not starting from zero — you've already met the security family. We covered the why of security and the modern powerhouses (OAuth2 + JWT) earlier. You also learned how Dependency Injection in FastAPI keeps your app clean and testable. Basic Auth sits lower on the ladder: small, useful for some internal tools, but definitely not the main character for public APIs.
Quick reminder: what Basic Authentication actually is
- Mechanics: The client sends credentials (username:password) encoded in Base64 in the HTTP header:
Authorization: Basic base64(username:password). - Important nuance: Base64 is encoding, not encryption. If someone can sniff traffic, they can recover the credentials instantly.
Why people still use it: it’s dead simple and easy to implement. Why you should be cautious: it transmits persistent raw credentials with every request — you’d rather not do that over the public internet without layers of protection.
When Basic Auth makes sense (and when it absolutely doesn't)
- Useful: internal services on a private network, quick prototypes, Docker registries in isolated environments, and tiny admin UIs behind TLS + VPN.
- Not useful: public APIs, multi‑tenant systems, third‑party integrations that need fine‑grained scopes, or anything requiring session invalidation or user delegation.
Think of Basic Auth as a blunt instrument. It works… until it doesn’t.
FastAPI cookbook: Implementing Basic Auth correctly (the sensible bits)
Here's a minimal, secure‑minded example using FastAPI's built‑in helpers and dependency injection.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
app = FastAPI()
security = HTTPBasic()
# Example user store (replace with DB + hashed passwords in prod)
_fake_users = {"alice": "$fake$hashed$password"}
def get_current_username(
credentials: HTTPBasicCredentials = Depends(security),
# db = Depends(get_user_db) # <-- integrate your DB via DI
):
# Use constant-time comparison to avoid timing attacks
is_correct_username = secrets.compare_digest(credentials.username, "alice")
is_correct_password = secrets.compare_digest(credentials.password, "secret")
if not (is_correct_username and is_correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get('/users/me')
def read_current_user(username: str = Depends(get_current_username)):
return {"username": username}
Key points in that snippet:
- We use
fastapi.security.HTTPBasicto parse theAuthorizationheader and provide aHTTPBasicCredentialsobject. secrets.compare_digestprevents simple timing attacks when comparing secrets.- The authentication logic is a dependency (
get_current_username) — this leverages FastAPI's DI and lets you reuse auth logic across endpoints.
Upgrade checklist for production (don’t skip these)
- Force HTTPS — Basic Auth without TLS is like whispering secrets into a megaphone.
- Never store passwords in plain text — use bcrypt / Argon2 or Passlib to hash and verify.
- Use constant‑time comparisons (we did this) to prevent timing attacks.
- Don't log credentials — logging
credentials.passwordis a career‑ending move. - Prefer token-based auth for public APIs — OAuth2/JWT gives you scopes, expiry, revocation patterns.
- Rate limit and monitor failed auth attempts — brute force will find weak passwords.
Example: verifying a hashed password using passlib (pseudocode):
from passlib.context import CryptContext
pwd_ctx = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain: str, hashed: str) -> bool:
return pwd_ctx.verify(plain, hashed)
# in your get_current_username: use verify_password(credentials.password, user.hashed_password)
Basic Auth vs OAuth2/JWT (the short table you’ll remember)
| Feature | Basic Auth | OAuth2 / JWT |
|---|---|---|
| Credential sent every request | Yes (username:password) | No (token) |
| Good for public APIs | No | Yes |
| Built‑in revocation | No (unless server changes password) | Yes (token revocation/refresh patterns) |
| Complexity | Minimal | Higher (auth server, token lifecycle) |
| Use case | Internal tools, simple apps | Public APIs, delegated auth |
Example: Combine Basic Auth with Dependency Injection (DB + Auth)
You learned DI earlier; here's how to plug DB access into your auth dependency.
def get_current_user(
credentials: HTTPBasicCredentials = Depends(security),
db = Depends(get_db),
):
user = db.get_user_by_username(credentials.username)
if not user or not verify_password(credentials.password, user.hashed_password):
raise HTTPException(...)
return user
This keeps your endpoint signatures clean and encourages testable code: mock get_db in tests, and you can test auth logic in isolation.
Quick reference: curl examples
With username/password flag:
curl -u alice:secret https://api.internal.example/users/me
Or manually set header (base64 of
alice:secret):curl -H "Authorization: Basic YWxpY2U6c2VjcmV0" https://api.internal.example/users/me
Final thoughts — the instructor’s mic drop
Basic Auth is honest and straightforward: send credentials, get access. That's its charm and its Achilles heel. Use it for internal magic when you control the network and can enforce TLS. But for public, modern, secure APIs — where you want scopes, expirations, revocation, and better UX — OAuth2/JWT (what you learned earlier) is the wiser choice.
Security isn't about picking the fanciest tool. It's about picking the right tool for the threat model, deploying it correctly (HTTPS, hashing, monitoring), and wiring it into your app cleanly (Dependency Injection, please). Basic Auth gets the job done sometimes — but treat it like a hammer, not a scalpel.
Key takeaways
- Basic Auth = Base64(username:password). Simple, but not encrypted.
- Always use TLS if you use Basic Auth.
- Integrate with DI to keep your auth logic testable and reusable.
- Prefer hashed passwords and secure comparisons; never store or log plaintext credentials.
- For public APIs, prefer OAuth2/JWT (more control, less exposure of raw credentials).
Go forth and authenticate responsibly. And if you ever see Authorization: Basic over plain HTTP in production: put on a helmet and call a security engineer.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!