Authorization
How Trovella uses CASL to enforce role-based and attribute-based access control across the application layer.
Authorization in Trovella is the application layer of a four-layer defense-in-depth model. It determines what an authenticated user is allowed to do within a specific organization, using CASL for declarative role-based permission checks and imperative attribute-based checks in router code.
How It Works
Every feature endpoint uses authorizedProcedure, which builds a CASL ability from the user's role and organization type. Router handlers then check ctx.ability.can(action, subject) before performing operations. This is the third layer in the defense model:
- Client -- UI elements hidden based on permissions (defense-in-depth, not security)
- Proxy -- route-level protection via Caddy
- Application -- CASL permission checks in tRPC routers (this topic)
- Database -- RLS policies enforce tenant isolation regardless of application logic
Pages in This Topic
CASL Decision
Why CASL was chosen over Casbin, Permit.io, Oso, and custom JWT claims. The ADR with full alternatives analysis and trade-offs.
Ability Definitions
The defineAbilityFor() function, its type signatures, the RBAC role switch, personal org restrictions, and the unknown-role fallback.
Role Permission Matrix
Complete matrix of what each role (owner, admin, member, default) can do with each subject, including personal org overrides.
Router Enforcement
How routers use ctx.ability for RBAC checks, how ABAC conditions are handled as imperative checks, and the architecture test that enforces authorizedProcedure usage.
Key Concepts
RBAC (role-based access control) handles 90% of authorization decisions. The user's role within an organization (owner, admin, or member) determines their permissions. CASL resolves these from the session with zero database lookups beyond the initial member record fetch.
ABAC (attribute-based access control) handles the remaining 10%. These are contextual checks like "an admin cannot modify an owner's role" or "a member can only remove themselves." ABAC conditions live in router code as explicit if statements, not in CASL's condition system.
Pure function ability builder. defineAbilityFor() takes { userId, role, orgType } and returns a CASL ability object. No database access, no side effects, deterministic output. This makes it safe to call in any context -- server, client, test -- and easy for AI agents to reason about.
Related Pages
- Data & Storage -- Tenant Scoping -- RLS policies (the database enforcement layer below this one)
- Data & Storage -- Procedure Chain -- the tRPC middleware chain that builds
ctx.ability - Data & Storage -- Tenant Context --
withTenantContextwrapper that opens the RLS-scoped transaction - Identity & Access -- Tenant Isolation -- the full defense-in-depth model across all four layers