Part 4: Security and Authentication in FastAPI
In this part of our FastAPI series, we delve into implementing robust security measures to protect your API endpoints. We'll cover basic authentication schemes, token-based authentication using OAuth2, and setting up role-based access control (RBAC).
Why Focus on Security?
Security is paramount for any application that interacts with user data or interfaces with other systems. A secure API guards against unauthorized access and ensures that user data is handled safely. FastAPI provides several tools to help secure your API effectively.
OAuth2 with Password and Bearer Tokens
One of the most common and secure ways to handle authentication in APIs is through OAuth2 with Password (and hashing), which involves token-based credentials.
Setting Up Authentication
First, let's set up the authentication scheme in FastAPI. We'll use OAuth2 with password flow, which is well-suited for securing web and mobile applications.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def fake_hash_password(password: str):
return "fakehashed" + password
class User(BaseModel):
username: str
email: Optional[str] = None
full_name: Optional[str] = None
disabled: Optional[bool] = None
class UserInDB(User):
hashed_password: str
# Assuming we have a way to fetch user details
def get_user(db, username: str):
if username == "johndoe":
return UserInDB(username="johndoe", hashed_password=fake_hash_password("secret"))
return None
# Authentication function
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user or user.hashed_password != fake_hash_password(password):
return False
return user
Creating a Token Endpoint
The token endpoint is where users will authenticate using their credentials to receive a token they can use for subsequent requests.
from fastapi import Security
from fastapi.security import OAuth2PasswordRequestForm
from jose import jwt, JWTError
SECRET_KEY = "a_very_secret_key"
ALGORITHM = "HS256"
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_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 = jwt.encode({"sub": user.username}, SECRET_KEY, algorithm=ALGORITHM)
return {"access_token": access_token, "token_type": "bearer"}
Role-Based Access Control (RBAC)
Role-based access control is a method to provide access based on the user role at an organization level. Let's implement a simple RBAC in FastAPI.
from fastapi import APIRouter, Depends, HTTPException
router = APIRouter()
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
except JWTError:
raise credentials_exception
user = get_user(fake_db, username=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
@router.get("/users/me", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user
Conclusion
Implementing robust authentication and role-based access control is crucial for securing a FastAPI application. By following these examples, you can set up a secure API that uses OAuth2 for authentication and implements simple RBAC to manage user access based on roles.
Stay tuned for more advanced topics in this series, where weβll explore testing strategies, integrate
If you would like to support me or buy me a beer feel free to join my Patreon jamesbmour
Top comments (0)