RBAC¶
Role-based access control with roles and fine-grained permissions. RBAC is opt-in: assign auth.role_adapter and it activates the /auth/roles admin endpoints and the require_role / require_permission dependencies.
Setup¶
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 that do not yet exist
yield
initialize_roles() only creates roles that are missing in the adapter — it never overwrites or deletes existing roles, so you can add permissions in code without touching production data.
Protecting routes¶
from fastauth.api.deps import require_role, require_permission
from fastauth.types import UserData
from fastapi import Depends
@app.get("/admin", dependencies=[Depends(require_role("admin"))])
async def admin(): ...
@app.get("/reports")
async def reports(user: UserData = Depends(require_permission("users:read"))):
return {...}
require_roleandrequire_permissionboth depend onrequire_auth.- The permission check passes if the user holds any role that includes the permission.
500 RBAC is not configuredis returned ifauth.role_adapter is None.
Admin endpoints¶
All routes below except /auth/roles/me are admin-gated via Depends(require_role("admin")).
| Method | Path | Request | Response |
|---|---|---|---|
| GET | /auth/roles |
— | [{name, permissions}] |
| POST | /auth/roles |
{name, permissions?} |
201 {name, permissions} (409 if exists) |
| DELETE | /auth/roles/{role_name} |
— | {message} (404 if missing) |
| POST | /auth/roles/{role_name}/permissions |
{permissions: [..]} |
{message} |
| DELETE | /auth/roles/{role_name}/permissions |
{permissions: [..]} |
{message} |
| POST | /auth/roles/assign |
{user_id, role_name} |
{message} |
| POST | /auth/roles/revoke |
{user_id, role_name} |
{message} |
| GET | /auth/roles/user/{user_id} |
— | {user_id, roles, permissions} |
| GET | /auth/roles/me |
— | {user_id, roles, permissions} |
Default role¶
assign_default_role(role_adapter, user_id, default_role) is called on:
POST /auth/register(credentials)POST /auth/magic-links/login(auto-registered users)- OAuth callback (newly created users)
…only when role_adapter and default_role are both set. Existing users created before you set default_role do not retroactively receive the role — assign it manually via /auth/roles/assign.
Common mistakes¶
500 RBAC is not configured—auth.role_adapterwas not set. Roles config alone does not enable RBAC.initialize_roles()not called in lifespan —/auth/rolesreturns an empty list (because no roles exist) on a fresh DB.- Admin tries to use
/auth/rolesbut is not admin — the routes are guarded byrequire_role("admin"). Bootstrap the first admin by directly inserting the role association in the DB, or temporarily comment out the route guard.