Difficulty: Advanced
How do you implement role-based access control in FastAPI?
RBAC in FastAPI is implemented using layered dependencies:
1. Authentication dependency - Extracts and validates the JWT, returns the user object. 2. Authorization dependency - Checks the user's role against the required role(s) for the route.
Common pattern: Create a `require_role()` function that returns a dependency. This dependency calls the auth dependency first, then checks if the user's role is in the allowed set.
For fine-grained permissions, use a permission model instead of roles: each route requires specific permissions (e.g., `can_delete_users`, `can_edit_posts`), and roles are collections of permissions.
from fastapi import FastAPI, Depends, HTTPException, status
from enum import Enum
from typing import Annotated
class Role(str, Enum):
ADMIN = "admin"
EDITOR = "editor"
VIEWER = "viewer"
# Auth dependency (returns user with role)
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = decode_token(token) # returns {"id": 1, "role": "admin"}
if not user:
raise HTTPException(status_code=401, detail="Invalid credentials")
return user
# Authorization dependency factory
def require_role(*roles: Role):
async def check_role(user: dict = Depends(get_current_user)):
if user["role"] not in [r.value for r in roles]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"Role {user['role']} not authorized"
)
return user
return check_role
app = FastAPI()
@app.delete("/users/{user_id}")
async def delete_user(
user_id: int,
user: dict = Depends(require_role(Role.ADMIN))
):
return {"deleted": user_id}
@app.put("/posts/{post_id}")
async def edit_post(
post_id: int,
user: dict = Depends(require_role(Role.ADMIN, Role.EDITOR))
):
return {"edited": post_id}
RBAC, permissions, roles, authorization, dependency