PHPackages                             hydrakit/authorization - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. hydrakit/authorization

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

hydrakit/authorization
======================

Authorization library for Hydra PHP framework

v0.2.0(yesterday)016↑25%2MITPHP &gt;=8.2

Since Jul 2Compare

[ Source](https://github.com/hydra-foundation/authorization)[ Packagist](https://packagist.org/packages/hydrakit/authorization)[ RSS](/packages/hydrakit-authorization/feed)WikiDiscussions Synced today

READMEChangelogDependencies (4)Versions (4)Used By (2)

Hydra Authorization
===================

[](#hydra-authorization)

Permissions — the sibling of `hydrakit/auth`. Auth answers *who you are*; authorization answers *what you may do*. It ships the gate that decides, but owns no policy of its own: the rules (abilities) are application policy and live in the app.

The gate
--------

[](#the-gate)

A controller injects `GateInterface` and asks `allows()`/`denies()`, or enforces a decision with `authorize()` (returns silently when allowed, throws a 403 `AuthorizationException` otherwise). Every method keys on an ability *class-string*, not a magic name:

```
$gate->authorize(EditNote::class, $note);   // throw 403 unless allowed
if ($gate->allows(AccessAdminPanel::class)) { /* ... */ }
```

The `Gate` composes the app-supplied ability with the *current* request's user (read from the auth guard) — there is no "authorize as someone else" path. It resolves the ability through the container, so abilities autowire, may inject app services (a repository, a clock), and need no separate registration.

Abilities (app policy)
----------------------

[](#abilities-app-policy)

`AbilityInterface` is the package's one extension point — the authorization twin of validation's `RuleInterface`. An app writes typed ability classes (`EditNote`, `AccessAdminPanel`) that inspect the user and an optional subject and return a plain yes/no:

```
final class EditNote implements AbilityInterface
{
    public function authorize(?AuthenticatableInterface $user, mixed $subject = null): bool
    {
        return $user !== null && $subject instanceof Note && $subject->ownerId === $user->getAuthIdentifier();
    }
}
```

The user is nullable: an unauthenticated visitor reaches abilities too, and the ability decides what that means (almost always: deny). Passing a class-string that isn't an `AbilityInterface` is a programming error and fails loudly, not a silent deny.

Enforcing an ability as middleware
----------------------------------

[](#enforcing-an-ability-as-middleware)

For a flat, subject-less gate ("admins only"), `authorize()` can move out of the controller and onto the route. `AuthorizeMiddleware` is abstract because a route names its middleware as a bare class-string with no per-route arguments — so the app extends it and fixes the ability, the same policy-in-the-app split as the abilities themselves:

```
final class RequireAdmin extends AuthorizeMiddleware
{
    protected function ability(): string { return AccessAdmin::class; }
}

#[RouteGroup('/admin', middleware: [AuthenticateMiddleware::class, RequireAdmin::class])]
```

Place `AuthenticateMiddleware` *outside* it, so an anonymous visitor gets a 401 login redirect rather than a 403 dead end. The check is deliberately subject-less: subject-bound authorization (`authorize(EditNote::class, $note)`) needs the record only the controller can load, so it stays an in-controller call. The app skeleton shows both halves together: `AdminController` rides `RequireAdmin` (flat role) on the route group, then calls `gate->authorize(ManageUser::class, $target)` in `edit`/`update`/`destroy` — the subject-bound rule (an admin may not delete or demote themselves) that depends on *which* user is being acted on, so it can't be a bare class-string middleware.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community5

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

3

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a947272da0bf1d7d6326b4fbf6ddb8049977cbdbe333d0d1282dbb6f5927dfdf?d=identicon)[whleucka](/maintainers/whleucka)

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/hydrakit-authorization/health.svg)

```
[![Health](https://phpackages.com/badges/hydrakit-authorization/health.svg)](https://phpackages.com/packages/hydrakit-authorization)
```

###  Alternatives

[vitalybaev/laravel5-dkim

Laravel 5/6 package for signing outgoing messages with DKIM.

3163.1k](/packages/vitalybaev-laravel5-dkim)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
