Files
atelier/domains/security/authorization.md
T
2026-08-05 00:30:31 +00:00

2.7 KiB

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