kinetis / authorization
Ability-based authorization for Kinetis — Gate wraps a callable Policy check, normalizing a bool/AuthorizationResponse result into an allow/deny decision and raising a denial as an exception that declares its own 403.
Requires
- php: ^8.4
- kinetis/framework: ^1.8.1
Requires (Dev)
- infection/infection: ^0.35.0
- phpstan/phpstan: ^2.2.8
- phpunit/phpunit: ^13.3.3
- vimeo/psalm: ^6.17
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
kinetis/authorization
Ability-based authorization for Kinetis
Part of Kinetis, a non-blocking PHP framework for API-first applications, developed in the kinetis-dev/kinetis monorepo.
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
Nothing to register. AuthorizationException declares its own 403
through core's Kinetis\Http\Exception\HttpStatusExceptionInterface,
so ExceptionHandlerMiddleware — included unconditionally — returns a
denied Gate::authorize() call as that response from any route.
Gate itself needs no explicit binding either: it has no constructor
dependencies, so plain autowiring resolves it wherever a controller
constructor-injects it.
Nothing else. There's no middleware, 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, which core returns as a403. Use it when a denial should hard-stop the request.allows($user, $check, ...$arguments): bool— reports the decision instead of acting on it: a denial isfalse, not an exception. 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(...)) { ... }).
Neither allows() nor denies() catches anything the check itself
throws — a check that fails outright is a failure, not a denial.
$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 and kinetis/framework.
Documentation:
kinetis.dev/docs/authorization.html
License
MIT — see LICENSE.