Last modified: Dec 02, 2025 By Alexander Williams
FastAPI OAuth2 Password Flow Security Guide
Security is vital for modern web APIs. FastAPI makes it easy to add robust authentication.
This guide covers the OAuth2 password flow. It is a common method for user login.
You will learn to secure endpoints and manage user sessions with JSON Web Tokens.
What is OAuth2 Password Flow?
OAuth2 is an authorization framework. It allows apps to access user data securely.
The password flow is suitable for trusted applications. Users provide credentials directly.
FastAPI has built-in OAuth2 support. It handles many security details for you.
Project Setup
First, create a virtual environment. Then install the required packages.
pip install fastapi uvicorn python-jose[cryptography] passlib[bcrypt] python-multipart
These packages provide core security functions. They handle tokens and password hashing.
Creating Pydantic Models
Define your data structures with Pydantic. This ensures validation and type safety.
from pydantic import BaseModel
from typing import Optional
class User(BaseModel):
username: str
email: Optional[str] = None
full_name: Optional[str] = None
disabled: Optional[bool] = None
class UserInDB(User):
hashed_password: str
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None
Models define the shape of user data and tokens. They are essential for FastAPI Validation with Pydantic Models.
Hashing User Passwords
Never store plain text passwords. Always use a strong hashing algorithm.
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
The CryptContext manages password hashing. The verify_password function checks credentials.
The get_password_hash function creates a secure hash for storage.
Simulating a User Database
For this example, we use a fake in-memory database. A real app needs a persistent store.
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW", # "secret"
"disabled": False,
}
}
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user
The get_user function retrieves a user by username. The authenticate_user function validates login attempts.
Creating JWT Access Tokens
JSON Web Tokens (JWT) are used for session management. They are signed and contain user data.
from datetime import datetime, timedelta
from jose import JWTError, jwt
SECRET_KEY = "your-secret-key-here-change-in-production"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
The create_access_token function generates a signed JWT. It includes an expiration time for security.
For a deeper dive, see our FastAPI JWT Authentication Python Tutorial.
Building the FastAPI Application
Now, integrate all components into the main FastAPI app. Define the login endpoint.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
The OAuth2PasswordBearer class defines the token endpoint. The login_for_access_token path operation handles user login.
It uses FastAPI Dependency Injection Best Practices for the form data.
Protecting API Endpoints
Create a dependency to get the current user from a token. Use it to secure routes.
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.get("/users/me/", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user
The get_current_user dependency decodes and validates the JWT. The get_current_active_user dependency checks if the user is active.
The read_users_me endpoint is now fully protected. It only returns data for authenticated, active users.
Testing the API
Run the server with Uvicorn. Then test the login and protected endpoints.
uvicorn main:app --reload
First, obtain a token by sending a POST request to /token.
curl -X POST "http://127.0.0.1:8000/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=johndoe&password=secret"
Expected output:
{"access_token":"eyJhbGciOiJIUzI1NiIs...","token_type":"bearer"}
Now, use the token to access the protected endpoint.
curl -X GET "http://127.0.0.1:8000/users/me/" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Expected output:
{"username":"johndoe","email":"[email protected]","full_name":"John Doe","disabled":false}
For comprehensive testing, learn about Test FastAPI Endpoints with pytest and TestClient.
Next Steps and Best Practices
This example uses a fake database. A real application needs a proper database setup.
Consider using FastAPI Database Migrations with Alembic to manage schema changes.
Always use environment variables for secrets like SECRET_KEY. Never hardcode them.
Increase token security with shorter expiration times. Implement refresh token logic.
For production deployment, follow our guide on Deploy FastAPI with Docker for Production.
Conclusion
FastAPI provides excellent tools for OAuth2 security. The password flow is straightforward to implement.
You learned to hash passwords, create JWTs, and protect endpoints. Always follow security best practices.
This foundation helps you build secure and reliable APIs. Your users' data will be safe.