PermissionDeniedException
The 403 signal that a request authenticated fine but is not *allowed* here — it does not hold the required {@see Permission}. Sibling of {@see ScopeDeniedException} (same {@see AuthException} base, same 403 shape) so error contracts do not move. The message names the required permission key (the developer's own policy, never a secret) and links the concept.
PermissionDeniedException::forRequired()
public static function forRequired(Milpa\Auth\Permission $required): selfBuilds the denial for a route that required `$required` and the actor did not hold it.
Parameters
| Name | Type | Description |
|---|---|---|
| $required | Milpa\Auth\Permission |