0
0
FastapiHow-ToBeginner · 4 min read

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.
  • OAuth2PasswordBearer or 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 HTTPException with 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 Depends to inject role-checking logic.
  • Define a reusable role_checker dependency factory.
  • Raise HTTPException with 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.