Role-based access control helps decide who can do what in your app. It keeps things safe by letting only the right people access certain parts.
Role-based access control in FastAPI
Start learning this pattern below
Jump into concepts and practice - no test required
or
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
Syntax
FastAPI
from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") async def get_current_user(token: str = Depends(oauth2_scheme)): # decode token and return user info pass def role_required(role: str): async def role_checker(user = Depends(get_current_user)): if role not in user.roles: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions") return user return role_checker app = FastAPI() @app.get("/admin") async def read_admin_data(user = Depends(role_required("admin"))): return {"msg": "Welcome admin!"}
Use Depends to check user roles before running endpoint code.
Define a function that returns a dependency checking the role.
Examples
FastAPI
def role_required(role: str): async def role_checker(user = Depends(get_current_user)): if role not in user.roles: raise HTTPException(status_code=403, detail="Forbidden") return user return role_checker
FastAPI
@app.get("/manager") async def manager_data(user = Depends(role_required("manager"))): return {"msg": "Hello manager!"}
Sample Program
This example shows a simple way to check user roles using tokens. Admins can access the /admin route, while all logged-in users can access /user.
FastAPI
from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import OAuth2PasswordBearer from typing import List app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") class User: def __init__(self, username: str, roles: List[str]): self.username = username self.roles = roles # Fake token to user mapping fake_users_db = { "token_admin": User("alice", ["admin", "user"]), "token_user": User("bob", ["user"]) } async def get_current_user(token: str = Depends(oauth2_scheme)): user = fake_users_db.get(token) if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials") return user def role_required(role: str): async def role_checker(user: User = Depends(get_current_user)): if role not in user.roles: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions") return user return role_checker @app.get("/admin") async def read_admin_data(user: User = Depends(role_required("admin"))): return {"message": f"Welcome admin {user.username}!"} @app.get("/user") async def read_user_data(user: User = Depends(get_current_user)): return {"message": f"Hello user {user.username}!"}
Important Notes
Always check user roles after verifying their identity.
Use HTTP 403 status code when access is denied due to roles.
Keep role checks reusable by creating a function that returns a dependency.
Summary
Role-based access control limits what users can do based on their roles.
FastAPI uses dependencies to check roles before running endpoint code.
Creating reusable role checkers keeps your code clean and safe.
Practice
1. What is the main purpose of role-based access control (RBAC) in FastAPI?
easy
Solution
Step 1: Understand RBAC concept
RBAC restricts what users can do depending on their roles, like admin or user.Step 2: Identify RBAC purpose in FastAPI
FastAPI uses RBAC to check user roles before allowing access to certain endpoints.Final Answer:
To limit user actions based on their assigned roles -> Option BQuick Check:
RBAC = limit actions by roles [OK]
Hint: RBAC controls user permissions by roles, not speed or docs [OK]
Common Mistakes:
- Confusing RBAC with performance optimization
- Thinking RBAC auto-generates docs
- Assuming RBAC manages database tasks
2. Which of the following is the correct way to declare a dependency that checks for an admin role in FastAPI?
easy
Solution
Step 1: Check dependency signature
def admin_required(user: User = Depends(get_current_user)): if user.role != 'admin': raise HTTPException(status_code=403) uses Depends to get current user, which is required for role checking.Step 2: Verify role check logic
def admin_required(user: User = Depends(get_current_user)): if user.role != 'admin': raise HTTPException(status_code=403) raises HTTP 403 if user is not admin, correctly enforcing access control.Final Answer:
def admin_required(user: User = Depends(get_current_user)): if user.role != 'admin': raise HTTPException(status_code=403) -> Option DQuick Check:
Depends + role check + HTTPException = def admin_required(user: User = Depends(get_current_user)): if user.role != 'admin': raise HTTPException(status_code=403) [OK]
Hint: Use Depends to get user, then check role and raise HTTPException [OK]
Common Mistakes:
- Not using Depends to get current user
- Checking wrong role or missing exception
- Returning True instead of raising exception
3. Given this FastAPI endpoint with role check dependency:
async def get_admin_data(admin: None = Depends(admin_required)):
return {"data": "secret"}
What happens if a user with role 'user' calls this endpoint?medium
Solution
Step 1: Understand admin_required behavior
admin_required raises HTTP 403 if user role is not 'admin'.Step 2: Apply to user role 'user'
User role 'user' is not 'admin', so HTTP 403 is raised before endpoint runs.Final Answer:
The endpoint raises HTTP 403 Forbidden error -> Option AQuick Check:
Non-admin user triggers 403 error [OK]
Hint: Non-admin roles cause 403 error before endpoint runs [OK]
Common Mistakes:
- Confusing 401 Unauthorized with 403 Forbidden
- Expecting endpoint to return data for non-admin
- Thinking empty response is returned
4. Identify the error in this FastAPI role check dependency:
def check_admin(user: User = Depends(get_current_user)):
if user.role == 'admin':
return True
else:
return False
@app.get('/admin')
async def admin_panel(is_admin: bool = Depends(check_admin)):
if not is_admin:
raise HTTPException(status_code=403)
return {"msg": "Welcome admin"}medium
Solution
Step 1: Analyze dependency behavior
check_admin returns True/False instead of raising HTTPException on failure.Step 2: Understand best practice for RBAC in FastAPI
Dependencies should raise HTTPException to stop execution early, not return bool flags.Final Answer:
Dependency should raise HTTPException directly, not return bool -> Option AQuick Check:
Raise exception in dependency, don't return bool [OK]
Hint: Raise HTTPException in dependency to block access immediately [OK]
Common Mistakes:
- Returning bool instead of raising exception
- Not stopping request early in dependency
- Misusing Depends inside dependencies
5. You want to create a reusable role checker in FastAPI that allows multiple roles (e.g., 'admin' or 'moderator') to access an endpoint. Which approach correctly implements this?
hard
Solution
Step 1: Understand reusable dependency pattern
def role_checker(allowed_roles: list[str]): def checker(user: User = Depends(get_current_user)): if user.role not in allowed_roles: raise HTTPException(status_code=403) return checker returns a function that checks if user role is in allowed_roles, raising HTTPException if not.Step 2: Verify logic for multiple roles
def role_checker(allowed_roles: list[str]): def checker(user: User = Depends(get_current_user)): if user.role not in allowed_roles: raise HTTPException(status_code=403) return checker correctly uses 'not in' to allow any role in the list, making it reusable.Final Answer:
def role_checker(allowed_roles: list[str]): def checker(user: User = Depends(get_current_user)): if user.role not in allowed_roles: raise HTTPException(status_code=403) return checker -> Option CQuick Check:
Reusable role check with allowed_roles list = def role_checker(allowed_roles: list[str]): def checker(user: User = Depends(get_current_user)): if user.role not in allowed_roles: raise HTTPException(status_code=403) return checker [OK]
Hint: Return inner function checking role in allowed_roles, raise HTTPException [OK]
Common Mistakes:
- Using incorrect logic with 'or' instead of 'in'
- Returning bool instead of raising exception
- Checking impossible conditions like role == 'admin' and 'moderator'
