kinetis / authorization
Ability-based authorization for Kinetis — Gate wraps a callable Policy check, normalizing a bool/AuthorizationResponse result and turning a denial into a 403 through one registered middleware.
Requires
- php: ^8.4
- kinetis/framework: ^1.1.1
Requires (Dev)
- infection/infection: ^0.35.0
- phpstan/phpstan: ^2.2.8
- phpunit/phpunit: ^12.5.33
- vimeo/psalm: ^6.16.1
README
kinetis/authorization
Ability-based authorization for Kinetis
Kinetis is deliberately unopinionated about how your application
organizes authorization checks — there's no required Policy convention,
no ability-name registry, and nothing here inspects an object's class to
decide which code answers a check. Gate is a small, generic wrapper:
hand it any callable and it normalizes the result into an allow/deny
decision.
use Kinetis\Authorization\Gate; final readonly class PostController { public function __construct( private Gate $gate, private PostPolicy $postPolicy, ) {} public function update(int $id, CurrentUserInterface $user): array { $post = $this->posts->find($id); $this->gate->authorize($user, $this->postPolicy->update(...), $post); // ... } }
$this->postPolicy->update(...) is PHP's own first-class callable
syntax — PostPolicy is a plain, constructor-injected class with plain
methods, resolved and called exactly like any other service. Gate never
sees PostPolicy exist as a concept.
Provides
Installing this package auto-registers, via extra.kinetis:
- A global middleware translating a thrown
AuthorizationExceptioninto a403response, so a deniedGate::authorize()call works from any route with nothing else to wire.
Gate itself needs no explicit binding — it has no constructor
dependencies, so plain autowiring resolves it wherever a controller
constructor-injects it.
Nothing else. There's no attribute to discover, no registry, and no
"Policy" concept this package enforces — PostPolicy above is only a
name a developer chose.
The three methods
authorize($user, $check, ...$arguments): void— throwsAuthorizationExceptionon denial, letting the registered middleware turn it into a403. Use it when a denial should hard-stop the request.allows($user, $check, ...$arguments): bool— never throws. Use it to branch, or to shape a response value ('canEdit' => $gate->allows(...)).denies($user, $check, ...$arguments): bool— the exact inverse ofallows(), for guard-clause style (if ($gate->denies(...)) { ... }).
$check is any callable(CurrentUserInterface, mixed...): bool|AuthorizationResponse
— a first-class callable reference to a method, a plain closure, or a
Gate::allows()-independent function. Returning a plain bool covers the
ordinary case; returning AuthorizationResponse::deny('a specific reason')
lets a denial carry a message more useful than the generic default.
$check may also be typed against a concrete CurrentUserInterface
implementation richer than the interface itself — kinetis/auth-jwt's
JwtUser, say, whose claim()/claims() already carry everything the
token decoded, with no query needed. Gate's methods are generic over the
user type (@template TUser of CurrentUserInterface) precisely so this
type-checks correctly. See
kinetis.dev/docs/authorization.html.
Installation
composer require kinetis/authorization
Requires PHP 8.4 or later. Documentation: kinetis.dev/docs/authorization.html
License
MIT — see LICENSE.