SECURITY FEATURE
Requires Permission
Access control should be declarative: the payload names the required permission, and the framework enforces it automatically.
Feature Guide
A quick orientation block that answers the essential questions: what this feature does, how it works, why it matters, and the key concepts behind it.
What this does
A payload can declare one required permission slug, and the framework enforces it before the handler is even called.
How it works
The authorization listener reads #[RequiresPermission] from the resolved payload, asks the authorizer for an access decision, and maps the result to 401 for guests or 403 for authenticated users missing the permission.
Why it matters
This keeps access control reviewable and removes defensive authorization code from handlers. The contract lives on the route boundary where reviewers expect to find it.
Key concepts
- #[RequiresPermission]
- Declares the exact permission slug required to execute the payload.
- 401 Unauthorized
- Returned when a guest subject hits a permission-protected route.
- 403 Forbidden
- Returned when the subject is authenticated but missing the declared permission.
- guard chain
- The authorization flow that evaluates the payload policy before the handler runs.
Permission Guard
One attribute, three outcomes
The same payload declaration produces different outcomes depending on whether the subject is a guest, authenticated without the permission, or authenticated with the permission.
#[RequiresPermission('users.manage')]
#[AsProtectedPayload(path: '/admin/users', methods: ['GET'])]
class ManageUsersPayload {}
final class ManageUsersHandler implements TypedHandlerInterface
{
public function handle(ManageUsersPayload $payload, AdminPageResource $resource): AdminPageResource
{
// No manual access checks here.
return $resource;
}
}
| Request state | Framework decision |
|---|---|
| Guest subject | 401 Unauthorized |
| Authenticated, missing permission | 403 Forbidden |
| Authenticated, permission granted | 200 OK |
Verified against Semitexa Ultimate 2026.09.19.1020
Requires Permission
Declare one permission slug on the payload and let the framework enforce it before your handler runs.
How it works
Place #[RequiresPermission('slug')] on any payload class. The guard chain intercepts every request to that route, resolves the current principal, and checks whether the permission is granted. Guests receive 401, authenticated subjects without the grant receive 403, and subjects with the grant reach the handler normally.
Why this matters
Access control should be declarative. When the permission requirement lives on the payload, it is visible to reviewers alongside the route definition, enforced consistently by the framework without any handler code, and impossible to accidentally skip by forgetting a manual check.
How it works
The authorization listener reads #[RequiresPermission] from the resolved payload, asks the authorizer for an access decision, and maps the result to 401 for guests or 403 for authenticated users missing the permission.
Why it matters
This keeps access control reviewable and removes defensive authorization code from handlers. The contract lives on the route boundary where reviewers expect to find it.
Key concepts
- #[RequiresPermission]
- Declares the exact permission slug required to execute the payload.
- 401 Unauthorized
- Returned when a guest subject hits a permission-protected route.
- 403 Forbidden
- Returned when the subject is authenticated but missing the declared permission.
- guard chain
- The authorization flow that evaluates the payload policy before the handler runs.