Role Based Access Control in FastAPI: Simple Implementation Guide
In
FastAPI, implement role based access by defining user roles and using Depends with security scopes or custom dependencies to check roles before allowing access to routes. Use OAuth2 scopes or a custom function to verify the user's role and raise HTTPException if unauthorized.Syntax
Role based access in FastAPI typically uses dependencies to check user roles before route execution. You define a dependency function that verifies the user's role and use Depends to enforce it on routes.
Key parts:
Depends: Injects the role-checking function into routes.OAuth2PasswordBeareror custom auth: Retrieves user info.HTTPException: Raised if user lacks required role.
python
from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") def get_current_user(token: str = Depends(oauth2_scheme)): # Decode token and return user info including roles return {"username": "alice", "roles": ["user", "admin"]} def role_checker(required_role: str): def checker(user: dict = Depends(get_current_user)): if required_role not in user["roles"]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Operation not permitted" ) return user return checker
Example
This example shows a FastAPI app with two routes: one open to all authenticated users and one restricted to admins only using role based access control.
python
from fastapi import FastAPI, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") def get_current_user(token: str = Depends(oauth2_scheme)): # Simulate token decoding and user retrieval if token == "admin-token": return {"username": "admin_user", "roles": ["admin", "user"]} elif token == "user-token": return {"username": "normal_user", "roles": ["user"]} else: raise HTTPException(status_code=401, detail="Invalid authentication credentials") def role_checker(required_role: str): def checker(user: dict = Depends(get_current_user)): if required_role not in user["roles"]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Operation not permitted" ) return user return checker @app.get("/open") def open_route(user: dict = Depends(get_current_user)): return {"message": f"Hello {user['username']}, you have access to open route."} @app.get("/admin") def admin_route(user: dict = Depends(role_checker("admin"))): return {"message": f"Hello {user['username']}, you have admin access."}
Output
GET /open with token 'user-token' returns {"message": "Hello normal_user, you have access to open route."}
GET /admin with token 'admin-token' returns {"message": "Hello admin_user, you have admin access."}
GET /admin with token 'user-token' returns 403 Forbidden error
Common Pitfalls
Common mistakes when implementing role based access in FastAPI include:
- Not verifying the token or user roles properly, allowing unauthorized access.
- Hardcoding roles inside route functions instead of using reusable dependencies.
- Not raising
HTTPExceptionwith correct status codes for unauthorized access. - Forgetting to secure the token endpoint or using insecure token handling.
Always centralize role checks in dependencies and test with different user roles.
python
from fastapi import Depends, HTTPException, status from fastapi import FastAPI app = FastAPI() def get_current_user(token: str = "fake-token"): # Dummy function for example return {"username": "user", "roles": ["user"]} # Wrong: role check inside route without dependency @app.get("/wrong-admin") def wrong_admin_route(user: dict = Depends(get_current_user)): if "admin" not in user["roles"]: return {"message": "Access denied"} # Should raise HTTPException instead return {"message": "Welcome admin"} # Right: use dependency to enforce role def role_checker(required_role: str): def checker(user: dict = Depends(get_current_user)): if required_role not in user["roles"]: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Operation not permitted") return user return checker @app.get("/right-admin") def right_admin_route(user: dict = Depends(role_checker("admin"))): return {"message": "Welcome admin"}
Quick Reference
Tips for role based access in FastAPI:
- Use
Dependsto inject role-checking logic. - Define a reusable
role_checkerdependency factory. - Raise
HTTPExceptionwith 403 status for unauthorized roles. - Secure token handling and user retrieval.
- Test routes with different user tokens and roles.
Key Takeaways
Use FastAPI dependencies to enforce role based access cleanly and reuse logic.
Always raise HTTPException with status 403 for unauthorized role access.
Retrieve user roles securely from tokens or session data before checking.
Centralize role checks in a dependency function for maintainability.
Test your role based routes with various user roles to ensure correct access control.