# Authorization — Derived Rules > Derives from `domains/security/first-principles.md` P2 (Least Privilege), P3 (Defense in Depth), P1 (Zero Trust). ## The Default: Deny - Every request is denied unless explicitly authorized. - "Authorized by default" is an anti-pattern. The absence of a rule means denial. - A missing authz check is a bug, not a feature gap. ## Authorization Models ### RBAC (Role-Based) - Users have roles; roles have permissions. - Roles are coarse: `admin`, `editor`, `viewer`. Permissions are fine: `post:create`, `post:delete`. - Check permissions, not roles: `can(user, 'post:create')`, not `user.role === 'admin'`. - Roles can change; permission checks are stable. ### ABAC (Attribute-Based) - Authorization based on attributes of the user, resource, and context. - More expressive: "user can edit a post if user.department == post.department and post.status == 'draft'". - Use when RBAC is too coarse. Beware: complex ABAC is hard to audit. ### ReBAC (Relationship-Based) - Authorization based on relationships (e.g., Zanzibel). - "user:alice is editor of document:42" — check the relationship graph. - Scales for fine-grained, resource-specific access (Google Docs-style). ## Where to Check (P4 Locality) - Check at the boundary: the API endpoint, the resolver, the controller. - Check at the data layer: defense in depth. A query that bypasses the controller still respects row-level security. - Never check only in the UI. The UI is a convenience, not a security boundary. ## Principle of Least Privilege (P2) - A token/role gets the minimum permissions to do its job. - No "admin" role for daily work. Admin is for administration; daily work uses a scoped role. - Service tokens are scoped to one service's resources, not "all resources." ## IDOR (Insecure Direct Object Reference) (P1 Zero Trust) - `/api/users/123` — does the requester own 123? Check. - Never assume the user can access any ID they request. The ID is input; inputs are untrusted. - Use scoped queries: `User.find({ id, owner: userId })`, not `User.find(id)`. ## Caching and Authz (P3 Defense in Depth) - Authorization is not cached across users. A cached response for user A is not served to user B. - Cache keys include the user/role, not just the resource. - "Cache it as public if anyone can see it" — only if truly anyone (no auth). ## What Violates Authorization | Violation | Principle | |-----------|-----------| | `/admin` endpoint with no authz check | P1 Zero Trust | | `user.role === 'admin'` instead of permission check | P2 (roles change) | | IDOR: `User.find(req.params.id)` with no ownership check | P1 Zero Trust | | Cached authz decision reused across users | P3 Defense in Depth | | Service token with "all resources" scope | P2 Least Privilege |