Fastapi Security
FastAPI has built-in support for various security tools, including common authentication and authorization methods such as OAuth2, JWT tokens, and API Keys. This section introduces how to implement user authentication based on OAuth2 + JWT.
* * *
## Overview of Security Authentication
FastAPI supports the following security schemes:
| Scheme | Use Case | Description |
| --- | --- | --- |
| HTTP Basic Auth | Simple internal services | Username and password are encoded in the request header with low security |
| API Key | Inter-service calls | Pass keys via request headers, query parameters, or cookies |
| OAuth2 + JWT | Front-end and back-end separated applications | Most commonly used scheme, secure and flexible |
* * *
## OAuth2 Password Flow + JWT
This is the most commonly used authentication scheme for front-end and back-end separated applications. The process:
1. The client sends username and password to the `/token` endpoint
2. The server verifies the credentials and returns a JWT access token
3. The client includes the token in subsequent requests (`Authorization: Bearer `)
4. The server validates the token to identify the user
### 1. Install Dependencies
pip install "python-jose" passlib
### 2. Complete Example
## Example
from datetime import datetime, timedelta
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel
# ===== Configuration =====
SECRET_KEY ="your-secret-key-keep-it-secret"# Use environment variables in production
ALGORITHM ="HS256"
ACCESS_TOKEN_EXPIRE_MINUTES =30
# ===== Password Hashing =====
pwd_context = CryptContext(schemes=, deprecated="auto")
# ===== OAuth2 Scheme =====
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# ===== Data Models =====
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str | None=None
class User(BaseModel):
username: str
email: str | None=None
full_name: str | None=None
disabled: bool | None=None
class UserInDB(User):
hashed_password: str
# ===== Mock Database =====
fake_users_db ={
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "alice@example.com",
"hashed_password": pwd_context.hash("secret"),# Password: secret
"disabled": False,
}
}
# ===== Utility Functions =====
def verify_password(plain_password: str, hashed_password: str) ->bool:
"""Verify password"""
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) ->str:
"""Generate password hash"""
return pwd_context.hash(password)
def get_user(db: dict, username: str) -> UserInDB | None:
"""Get user from database"""
if username in db:
return UserInDB(**db)
return None
def authenticate_user(db: dict, username: str, password: str):
"""Validate user credentials"""
user= get_user(db, username)
if not user:
return False
if not verify_password(password,user.hashed_password):
return False
return user
def create_access_token(data: dict, expires_delta: timedelta | None=None):
"""Create JWT access token"""
to_encode = data.copy()
expire =datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
"""Get current user from token (dependency function)"""
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=)
username: str= payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user= get_user(fake_users_db, username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(
current_user: Annotated[User, Depends(get_current_user)],
):
"""Get current active user"""
if current_user.disabled:
raise HTTPException(status_code=400, detail="User is disabled")
return current_user
# ===== Routes =====
app = FastAPI()
@app.post("/token")
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
"""Login to get token"""
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"}
@app.get("/users/me")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
"""Get current user info (requires authentication)"""
return current_user
@app.get("/users/me/items")
async def read_own_items(
current_user: Annotated[User, Depends(get_current_active_user)],
):
"""Get current user's items (requires authentication)"""
return[{"item_id": "Foo","owner": current_user.username}]
* * *
## Code Explanation
### Password Hashing
Use `passlib`'s bcrypt algorithm to hash passwords, ensuring that plaintext passwords are not stored in the database:
| Function | Description |
| --- | --- |
| `pwd_context.hash(password)` | Convert plaintext password to hash |
| `pwd_context.verify(plain, hashed)` | Verify if plaintext password matches hash |
### JWT Tokens
JWT (JSON Web Token) is a secure token format that contains user information and expiration time:
| Operation | Function | Description |
| --- | --- | --- |
| Create token | `jwt.encode()` | Encode data into a JWT string |
| Decode token | `jwt.decode()` | Parse and validate JWT string |
| Set expiration | `"exp": expire` | Expiration time of the token |
| Store user identifier | `"sub": username` | Subject of the token (usually username) |
### OAuth2PasswordBearer
Tells FastAPI to extract the token from the `Authorization: Bearer ` request header:
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
`tokenUrl="token"` specifies the endpoint path where clients can obtain the token, which will appear in the API documentation.
> The `SECRET_KEY` must be kept secret and sufficiently complex. In production environments, it should be stored using environment variables. If the key is leaked, attackers can forge tokens for any user.
* * *
## Testing with API Documentation
After configuring OAuth2, the Swagger UI will show an "Authorize" button:
1. Click **"Authorize"**
2. Enter username and password (e.g., `alice` / `secret`)
3. Click **"Authorize"** to get the token
4. All subsequent requests will automatically include the token
* * *
## Summary
* OAuth2 + JWT is the most commonly used authentication scheme for front-end and back-end separated applications
* Passwords must be hashed and never stored in plain text
* JWT tokens contain user identifiers and expiration times
* Use dependency injection (`Depends`) to reuse authentication logic
* The SECRET_KEY must be kept secret in production environments
YouTip