Table of Contents
6. Authorization & Access Control — Keeping Capabilities in Their Lane
Authentication answers who the user is.
Authorization answers what the user can do.
A secure system treats capabilities as boundaries
— not conveniences.
Every action, every resource, every piece of data has a lane,
and users must stay within it.
This page introduces the mental model for
designing authorization that is predictable, explicit, and difficult to misuse.
1. Authorization Is About Capabilities, Not Roles
Roles are labels.
Capabilities are actions.
A secure system focuses on:
- what the user can do
- what the user can access
- what the user can modify
- what the user can see
Roles are just a way
to group capabilities.
Capabilities are the real boundary.
2. Prefer Capability‑Based Access Over Role‑Based Access
Role‑based access control (RBAC) is simple,
but often too broad.
Capability‑based access control (CBAC) is:
- more explicit
- more flexible
- more secure
- easier to reason about
Example:
Instead of:
"Admin can manage users"
Use:
"CanCreateUser" "CanEditUser" "CanDisableUser" "CanResetPassword"
Capabilities are small, clear, and testable.
3. Authorization Must Be Checked at the Boundary
Authorization belongs at the entry point of an action:
- controller
- handler
- service boundary
- API endpoint
Do not:
- check authorization deep inside business logic
- rely on UI controls to hide actions
- assume the user “won’t try that”
Every action must enforce its own boundary.
4. Avoid “Soft” Authorization
Soft authorization is when the system:
- hides buttons
- hides links
- hides UI elements
…but does not enforce the rule on the backend.
That's not security.
That's convenience.
A secure system:
- hides the button
- and enforces the rule on the server
- UI is not a security boundary.
5. Authorization Must Be Explicit, Not Implicit
Avoid implicit rules like:
"Admins can do everything"
"If the user created it, they can edit it"
"If the ID matches, it’s allowed"
Implicit rules drift.
Explicit rules endure.
Prefer:
if (!$auth->can('edit_post', $post)) { throw new UnauthorizedException(); }
Explicit authorization is readable, testable, and predictable.
6. Use Policy Objects for Clarity
Policies encapsulate authorization logic.
Example:
class PostPolicy { public function edit(User $user, Post $post): bool { return $user->id === $post->author_id; } }
This keeps authorization:
- centralized
- testable
- consistent
- easy to reason about
Policies are the architectural home for capability rules.
7. Avoid Overloading the Session With Authorization Data
Do not store:
- roles
- permissions
- capabilities
…inside the session.
Why?
- they can drift
- they can become stale
- they can be manipulated
- they break revocation
Always fetch authorization data fresh from a trusted source.
8. Authorization Must Support Revocation
A secure system must be able to say:
"This user no longer has this capability." "This role has changed." "This permission is revoked."
Revocation must take effect immediately.
This is why authorization data must not be cached in:
- sessions
- JWTs (JSON Web Tokens)
- cookies
- client‑side storage
Authorization is dynamic.
9. Least Privilege Is the Default Posture
A secure system gives users:
- the minimum capabilities they need
- no more
- no exceptions
- no “temporary” privileges that never get removed
Least privilege reduces risk by reducing surface area.
10. Authorization Is Not Authentication
Common mistakes:
- treating user ID as permission
- assuming “logged in” means “allowed”
- using authentication tokens as authorization tokens
Authentication is identity.
Authorization is capability.
They must remain separate.
Summary
Authorization is the discipline of keeping capabilities in their lane.
A secure system:
- focuses on capabilities, not roles
- checks authorization at boundaries
- avoids soft authorization
- uses explicit, testable rules
- centralizes logic in policies
- avoids storing permissions in sessions
- supports immediate revocation
- follows least privilege
- treats authorization as distinct from authentication
Security is not about trusting the user —
it’s about trusting the boundaries.
This page describes secure defaults and common practices in modern PHP. It is not a complete security guide, and it does not replace formal audits or framework‑specific documentation. These principles are consistent enough to be useful, but security always requires context, judgment, and ongoing review.
