Protecting routes¶
FastAuth exposes three FastAPI dependencies in fastauth.api.deps. They read the access token from the Authorization: Bearer header or the access_token cookie, and return the authenticated UserData.
require_auth¶
from fastapi import Depends
from fastauth.api.deps import require_auth
from fastauth.types import UserData
@app.get("/profile")
async def profile(user: UserData = Depends(require_auth)):
return {"email": user["email"], "id": user["id"]}
Behavior (api/deps.py):
- Token from header or cookie. Decoded via
core.tokens.decode_token. typeclaim must be"access"; otherwise treated as unauthenticated (no error raised insideget_current_user—require_auththen raises401).- The user is loaded via
adapter.get_user_by_id. If the user is missing oris_active=False,require_authraises401. - If the token came from a cookie, and the request method is POST/PUT/PATCH/DELETE, the CSRF double-submit check runs: the
X-CSRF-Tokenheader must equal thecsrf_tokencookie (constant-time compared). Mismatch →403.
Using it with dependencies=¶
You can attach the dependency to a whole router or method without receiving the user:
from fastapi import APIRouter, Depends
router = APIRouter(dependencies=[Depends(require_auth)])
@router.get("/things")
async def list_things(): ...
require_role¶
Requires RBAC: auth.role_adapter must be set on the FastAuth instance.
from fastauth.api.deps import require_role
@app.get("/admin")
async def admin_area(user: UserData = Depends(require_role("admin"))):
return {"user_id": user["id"]}
401if not authenticated.403 Insufficient roleif the user does not hold the role.500 RBAC is not configuredifauth.role_adapter is None.
require_permission¶
from fastauth.api.deps import require_permission
@app.get("/reports")
async def reports(user: UserData = Depends(require_permission("reports:read"))):
return {...}
Passes if the user holds any role containing the permission string. Same failure codes as require_role.
Wiring RBAC¶
config = FastAuthConfig(
...,
roles=[
{"name": "admin", "permissions": ["users:read", "users:write", "users:delete"]},
{"name": "user", "permissions": ["profile:read", "profile:write"]},
],
default_role="user",
)
auth = FastAuth(config)
auth.role_adapter = adapter.role # required
@asynccontextmanager
async def lifespan(app: FastAPI):
await adapter.create_tables()
await auth.initialize_roles() # seeds roles from config.roles
yield
See RBAC for the /auth/roles admin endpoints.
Combining dependencies¶
require_role and require_permission already depend on require_auth. Do not nest them manually — passing both to Depends re-decodes the token twice. Use one of the three.
Common mistakes¶
500 RBAC is not configured— you forgotauth.role_adapter = adapter.role. Settingroles=in the config alone is not enough.- Roles exist but user has none — you did not call
await auth.initialize_roles()in the lifespan, or the user was created beforedefault_rolewas set. Existing users do not retroactively receivedefault_role. 403 CSRF token missing or invalid— you are using cookie delivery and did not sendX-CSRF-Tokenmatching thecsrf_tokencookie on a POST/PUT/PATCH/DELETE.- Sending the refresh token as a Bearer — only access tokens are accepted by
require_auth. Refresh withPOST /auth/refreshfirst.