Skip to content

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_role and require_permission both depend on require_auth.
  • The permission check passes if the user holds any role that includes the permission.
  • 500 RBAC is not configured is returned if auth.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 configuredauth.role_adapter was not set. Roles config alone does not enable RBAC.
  • initialize_roles() not called in lifespan/auth/roles returns an empty list (because no roles exist) on a fresh DB.
  • Admin tries to use /auth/roles but is not admin — the routes are guarded by require_role("admin"). Bootstrap the first admin by directly inserting the role association in the DB, or temporarily comment out the route guard.