FastAPI OAuth2 and Password Flow Authentication
Authentication answers the question: "Who are you?" Before your API gives access to protected data, it must verify the caller's identity. OAuth2 Password Flow is the most common pattern for username/password login in FastAPI applications.
How OAuth2 Password Flow Works
Step 1: User sends username + password to /token endpoint
│
▼
Step 2: Server verifies credentials against database
│
▼
Step 3: Server returns an access token (a long random string)
│
▼
Step 4: User sends token in Authorization header for all future requests
│
▼
Step 5: Server reads token, identifies user, allows or denies access
Install Password Hashing Library
pip install "passlib[bcrypt]"
Never store plain text passwords. Always store a hashed version. bcrypt is the industry standard hashing algorithm for passwords.
Setting Up Password Hashing
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(plain: str) -> str:
return pwd_context.hash(plain)
def verify_password(plain: str, hashed: str) -> bool:
return pwd_context.verify(plain, hashed)
Plain password: "mySecret123"
Hashed version: "$2b$12$KIX3..." (60+ chars, different every time)
verify_password("mySecret123", "$2b$12$KIX3...") → True
verify_password("wrongPass", "$2b$12$KIX3...") → False
The Login Endpoint
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Fake database for this example
fake_users = {
"meera": {"username": "meera", "hashed_password": hash_password("secret123")}
}
@app.post("/token")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = fake_users.get(form_data.username)
if not user:
raise HTTPException(status_code=401, detail="Incorrect username")
if not verify_password(form_data.password, user["hashed_password"]):
raise HTTPException(status_code=401, detail="Incorrect password")
return {"access_token": form_data.username, "token_type": "bearer"}
OAuth2PasswordRequestForm reads username and password from form data — which is the standard OAuth2 format for the token endpoint.
Reading the Token on Protected Routes
@app.get("/me")
def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_users.get(token)
if user is None:
raise HTTPException(status_code=401, detail="Invalid token")
return {"username": user["username"]}
Client request: GET /me Authorization: Bearer meera FastAPI extracts "meera" from the header → looks it up in fake_users → returns user info
What oauth2_scheme Does
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
It does two things:
1. Tells the /docs page where the login form is (tokenUrl="token")
2. Extracts the Bearer token from the Authorization header
and passes it to your function as a string
The /docs Login Button
With OAuth2PasswordBearer configured, the Swagger UI at /docs shows an "Authorize" button. Click it, enter username and password, and the docs page sends the token automatically with every test request. This makes manual API testing much easier.
Token Storage on the Client Side
Web app (browser):
Store token in memory (safest) or localStorage.
Send in every request header:
Authorization: Bearer eyJhbGc...
Mobile app:
Store in secure storage (Keychain on iOS, Keystore on Android).
Same header format.
Key Points
- OAuth2 Password Flow uses username + password to get a token, then the token for all future requests.
- Always hash passwords with bcrypt — never store plain text.
- Use
OAuth2PasswordRequestFormto read login credentials from form data. - Use
OAuth2PasswordBearerto extract the Bearer token from request headers. - The next topic covers JWT tokens, which make the token itself carry user information securely.
