FastAPI Interview Questions 36-40 (Security Scopes, Roles, & Async)
You have built the API, connected the database, and secured the login. Now, how do you control who can do what? Not every logged-in user should be able to delete products or ban users.
In this section, we will implement Role-Based Access Control (RBAC)using OAuth2 scopes. We will also look at the proper way to store passwords (hashing) and wrap up with a deep dive into how FastAPI's async engine actually works under the hood.
36. What are security scopes?
OAuth2 Scopes are a way to limit permissions. Instead of just checking "Is this user logged in?", you check "Does this user have the 'items:write' permission?"
Analogy: The Keycard Access Level
Your employee ID card (Token) gets you into the building (Authentication). But to enter the Server Room (Sensitive Endpoint), your card needs a specific authorization code (Scope) encoded on it.
In FastAPI, you declare the required scopes in the Security dependency.
from fastapi import FastAPI, Depends, Security
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token",
scopes={"me": "Read own data", "items": "Read items"} # Documentation
)
def get_current_user(
security_scopes: SecurityScopes,
token: str = Depends(oauth2_scheme)
):
# 1. See what scopes the endpoint requires
required_scopes = security_scopes.scopes
# 2. Decode token and check if user has them
# (Mock logic)
user_scopes = ["me"]
for scope in required_scopes:
if scope not in user_scopes:
raise HTTPException(status_code=401, detail="Not enough permissions")
return {"username": "alice", "scopes": user_scopes}
# Endpoint requires 'items' scope
@app.get("/items/", dependencies=[Security(get_current_user, scopes=["items"])])
def read_items():
return [{"item": "Sword"}]
37. How do you implement JWT authentication in FastAPI?
We discussed the flow in Question 34, but here is the implementation detail using the python-jose library.
Key steps:
1. Install pip install python-jose[cryptography].
2. Define a secret key and algorithm (HS256).
3. Create a function to encode data into a token.
from datetime import datetime, timedelta
from jose import jwt
SECRET_KEY = "mysecretkey"
ALGORITHM = "HS256"
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
# Add expiration time to payload
to_encode.update({"exp": expire})
# Sign the token
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# Usage
token = create_access_token(
data={"sub": "alice"},
expires_delta=timedelta(minutes=30)
)
print(token)
# Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
38. How do you handle password hashing securely?
Never store plain text passwords.FastAPI recommends using Passlib with theBcrypt algorithm.
Bcrypt is slow on purpose. This makes it very hard for hackers to brute-force passwords even if they steal your database.
# pip install passlib[bcrypt]
from passlib.context import CryptContext
# Create a context wrapper for hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# 1. Hash a password (Registration)
def get_password_hash(password):
return pwd_context.hash(password)
# 2. Verify a password (Login)
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# Example
hashed = get_password_hash("secret123")
print(hashed)
# Output: $2b$12$EixZaYVK1fsbw1ZfbX3OXePaWrn96pzbPnQE...
is_match = verify_password("secret123", hashed)
print(is_match) # Output: True
39. How do you manage user roles and permissions in FastAPI?
While OAuth2 scopes are standard, sometimes you need simpler Role-Based Access Control (RBAC) like "Admin" vs "User".
You can implement this by creating a custom dependency that checks the user object directly.
from fastapi import HTTPException, Depends
# Assume 'get_current_user' returns a user dict/object from the DB
def get_current_user():
return {"username": "bob", "role": "user"}
class RoleChecker:
def __init__(self, allowed_roles: list):
self.allowed_roles = allowed_roles
def __call__(self, user: dict = Depends(get_current_user)):
if user["role"] not in self.allowed_roles:
raise HTTPException(status_code=403, detail="Operation not permitted")
return user
# Usage in a route
allow_admin = RoleChecker(["admin"])
allow_manager = RoleChecker(["admin", "manager"])
@app.delete("/users/{id}", dependencies=[Depends(allow_admin)])
def delete_user(id: int):
return {"msg": "User deleted"}
Sample Output (for user "bob"):
{
"detail": "Operation not permitted"
}40. How does FastAPI support asynchronous programming?
This is the technical deep-dive question. FastAPI runs on an Event Loop(provided by asyncio).
Key Concept:
When you define a function with async def, FastAPI runs it directly on the event loop. When you define it with just def, FastAPI runs it in a separate thread pool.
- Use `async def`: If your code uses
await(e.g., calling async DBs, external APIs). - Use `def`: If your code performs blocking IO (e.g., reading a file, using standard
requestslibrary, standardtime.sleep).
import time
import asyncio
# 1. Async Endpoint (Non-blocking)
@app.get("/async")
async def async_endpoint():
# 'await' yields control, allowing other requests to run
await asyncio.sleep(1)
return {"msg": "I am fast"}
# 2. Sync Endpoint (Blocking but Threaded)
@app.get("/sync")
def sync_endpoint():
# FastAPI runs this in a thread, so it doesn't block the main loop
time.sleep(1)
return {"msg": "I am compatible"}
Note: Be careful not to mix them up. If you write async def but use time.sleep(10) inside it, you will freeze the entire server for 10 seconds for everyone.