PHPackages                             sirix/mezzio-rbac - 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. sirix/mezzio-rbac

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

sirix/mezzio-rbac
=================

RBAC authorization package for Mezzio framework with optional attribute-based support

1.0.0(2mo ago)0434MITPHPPHP ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0CI passing

Since May 8Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/sirix777/mezzio-rbac)[ Packagist](https://packagist.org/packages/sirix/mezzio-rbac)[ Fund](https://buymeacoffee.com/sirix)[ GitHub Sponsors](https://github.com/sirix777)[ RSS](/packages/sirix-mezzio-rbac/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (4)Dependencies (9)Versions (6)Used By (0)

Mezzio RBAC
===========

[](#mezzio-rbac)

[![Latest Stable Version](https://camo.githubusercontent.com/3dedb3652041ca84389a1438d7701440a342f5c69634b262715da1fd051a9dec/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726261632f76)](https://packagist.org/packages/sirix/mezzio-rbac) [![Total Downloads](https://camo.githubusercontent.com/38381aa32141385b7968136a95f4a0523964de612ec7d656ff43730cc9aec9b8/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726261632f646f776e6c6f616473)](https://packagist.org/packages/sirix/mezzio-rbac) [![Latest Unstable Version](https://camo.githubusercontent.com/2a7264850cb8b4464c519b1c0ff71398263b2259569baf2fba6e74940e6698d2/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726261632f762f756e737461626c65)](https://packagist.org/packages/sirix/mezzio-rbac) [![License](https://camo.githubusercontent.com/3ec57dc4d9a701402eb2a2c3194c4bfbdad00cd76054c571bedc7d88b3dfe125/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726261632f6c6963656e7365)](https://packagist.org/packages/sirix/mezzio-rbac) [![PHP Version Require](https://camo.githubusercontent.com/22d497ce0d1443e5c3775672020e251b5ca2e533ce3ba0adbaf8d03d837a5df0/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726261632f726571756972652f706870)](https://packagist.org/packages/sirix/mezzio-rbac)

RBAC authorization package for Mezzio with PSR-15 middleware and optional PHP attribute integration.

Installation
------------

[](#installation)

```
composer require sirix/mezzio-rbac
```

Package is auto-registered via `extra.laminas.config-provider`.

Core Concepts
-------------

[](#core-concepts)

### Actor

[](#actor)

Current subject is represented by `ActorInterface`.

```
use Sirix\Mezzio\Rbac\Actor\Actor;

$actor = new Actor(['editor', 'moderator']);
```

Guest fallback is provided by `Sirix\Mezzio\Rbac\Actor\GuestActor`.

### Authorization paths

[](#authorization-paths)

The package has two authorization paths:

Use caseServiceHTTP request authorization`RequestGuardInterface` / `AuthorizeMiddleware`Non-HTTP service or CLI authorization`GuardInterface``GuardInterface` remains request-independent. It uses `ActorProviderInterface` and is useful from services, CLI commands, or application-managed contexts.

`AuthorizeMiddleware` uses `RequestGuardInterface` so it can authorize against the actor stored on the current PSR-7 request.

### Guard

[](#guard)

Main non-HTTP authorization entrypoint:

```
use Sirix\Mezzio\Rbac\Contract\GuardInterface;

$guard->allows('posts.update');
$guard->denies('admin.panel');
$guard->authorize('posts.delete');
```

`authorize()` throws `Sirix\Mezzio\Rbac\Exception\AuthorizationException` with HTTP status `403`.

### Request Guard

[](#request-guard)

HTTP-aware authorization entrypoint:

```
use Psr\Http\Message\ServerRequestInterface;
use Sirix\Mezzio\Rbac\Contract\RequestGuardInterface;

final readonly class PostHandler
{
    public function __construct(private RequestGuardInterface $guard) {}

    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $this->guard->authorize($request, 'posts.update', [
            'postId' => $request->getAttribute('id'),
        ]);

        // ...
    }
}
```

For route-level protection, prefer `AuthorizeMiddleware` or `#[Can]`.

### Permissions

[](#permissions)

Permissions use dot-notation and wildcard matching:

- `posts.read`
- `posts.update`
- `admin.users.delete`
- `posts.*` (greedy match)
- `admin.*.delete` (exact segment count)

Example:

```
use Sirix\Mezzio\Rbac\Contract\PermissionsInterface;
use Sirix\Mezzio\Rbac\Rule\ForbidRule;

$permissions->addRole('editor');
$permissions->associate('editor', 'posts.*');
$permissions->associate('editor', 'posts.delete', ForbidRule::class);
```

Resolution rules:

- exact match beats wildcard;
- more specific wildcard beats broader wildcard;
- latest association wins when specificity is equal;
- another actor role may still grant access if one role forbids it.

### Conflict Resolution: Allow wins over Deny

[](#conflict-resolution-allow-wins-over-deny)

The package follows an "Allow wins over Deny" policy. If an actor has multiple roles, access is granted if **at least one** role allows the permission.

Example: if a user has both `user` allowed `posts.read` and `banned` forbidden `posts.read`, the user still has access because the `user` role grants it.

### Wildcard Matching

[](#wildcard-matching)

Permissions use dot-notation and support greedy terminal wildcard matching:

- `posts.*` matches `posts.read`, `posts.update`, and nested resources like `posts.read.history`.
- `admin.*` grants access to all sub-resources of any depth.
- Non-terminal wildcards, for example `admin.*.delete`, still require exact segment positioning.

Rules
-----

[](#rules)

Built-in rules:

- `Sirix\Mezzio\Rbac\Rule\AllowRule`
- `Sirix\Mezzio\Rbac\Rule\ForbidRule`

Custom rules implement `Sirix\Mezzio\Rbac\Contract\RuleInterface`:

```
use Sirix\Mezzio\Rbac\Contract\ActorInterface;
use Sirix\Mezzio\Rbac\Contract\RuleInterface;

final class OwnPostRule implements RuleInterface
{
    public function allows(ActorInterface $actor, string $permission, array $context): bool
    {
        return ($context['ownerId'] ?? null) === ($context['userId'] ?? null);
    }
}
```

Then associate it with a permission:

```
$permissions->associate('user', 'posts.update', OwnPostRule::class);
```

HTTP Integration
----------------

[](#http-integration)

### Actor resolution for HTTP requests

[](#actor-resolution-for-http-requests)

`AuthorizeMiddleware` resolves the actor from the current request through `RequestActorProviderInterface`.

The default provider reads this request attribute:

```
'rbac' => [
    'request_actor_attribute' => 'sirix.authentication.actor',
]
```

This default matches `sirix/mezzio-authentication`, which stores the authenticated actor in `sirix.authentication.actor`.

If the request attribute contains an RBAC `ActorInterface`, it is used directly. If it contains an authentication-like object with `getRoles()`, it is adapted to an RBAC actor. Missing or invalid actor values fall back to `GuestActor`.

`ContainerActorProvider` is still available for non-request usage through `GuardInterface`, but it should not be used to resolve the current HTTP user.

### Metadata resolution

[](#metadata-resolution)

`AuthorizeMiddleware` resolves permission metadata in this order:

1. request attribute `sirix.rbac.permission`;
2. matched route option `sirix.rbac.permission`;
3. if missing or empty, pass through without authorization.

Context follows the same order:

1. request attribute `sirix.rbac.context`;
2. matched route option `sirix.rbac.context`;
3. empty array.

Context values map request attributes into rule context:

```
[
    'postId' => 'id', // context['postId'] = $request->getAttribute('id')
]
```

### With standard Mezzio routing

[](#with-standard-mezzio-routing)

Register `AuthorizeMiddleware` in your route pipeline and set permission/context as route options:

```
use Sirix\Mezzio\Rbac\Middleware\AuthorizeMiddleware;
use Sirix\Mezzio\Rbac\RbacAttribute;

$app->post('/posts/:id', [
    AuthorizeMiddleware::class,
    PostHandler::class,
], 'post.update')->setOptions([
    RbacAttribute::Permission->value => 'posts.update',
    RbacAttribute::Context->value => ['postId' => 'id'],
]);
```

You can also set request attributes before `AuthorizeMiddleware` runs. Request attributes take precedence over route options.

### With `sirix/mezzio-routing-attributes`

[](#with-sirixmezzio-routing-attributes)

When used with `sirix/mezzio-routing-attributes:^1.0`, `#[Can]` implements `RouteAttributeModifierInterface`. It injects `AuthorizeMiddleware` into the route pipeline and stores permission/context in route options.

```
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Sirix\Mezzio\Rbac\Attribute\Can;
use Sirix\Mezzio\Routing\Attributes\Attribute\Post;

#[Post('/posts/:id', name: 'post.update')]
#[Can('posts.update', ['postId' => 'id'])]
final class PostHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        // Authorization has already run before the handler.
    }
}
```

No manual middleware registration is needed for routes discovered by `sirix/mezzio-routing-attributes`.

Integration with `sirix/mezzio-authentication`
----------------------------------------------

[](#integration-with-sirixmezzio-authentication)

`sirix/mezzio-authentication` writes the current actor to request attribute `sirix.authentication.actor`. RBAC uses that attribute by default, so the usual route pipeline is:

```
AuthenticateMiddleware
  -> request attribute sirix.authentication.actor
  -> AuthorizeMiddleware
  -> RequestGuard
  -> permission lookup/rules

```

With attributes:

```
use Sirix\Mezzio\Authentication\Attribute\Authenticated;
use Sirix\Mezzio\Rbac\Attribute\Can;
use Sirix\Mezzio\Routing\Attributes\Attribute\Get;

#[Get('/admin', name: 'admin')]
#[Authenticated]
#[Can('admin.access')]
final class AdminHandler implements RequestHandlerInterface
{
    // ...
}
```

Expected behavior:

- anonymous user is stopped by authentication;
- authenticated non-admin user receives `403`;
- authenticated admin user receives `200`.

Manual authorization from services
----------------------------------

[](#manual-authorization-from-services)

For services without a request, use `GuardInterface`:

```
use Sirix\Mezzio\Rbac\Contract\GuardInterface;

final readonly class PostService
{
    public function __construct(private GuardInterface $guard) {}

    public function deletePost(string $postId): void
    {
        $this->guard->authorize('posts.delete', [
            'postId' => $postId,
        ]);
    }
}
```

Storage Boundary
----------------

[](#storage-boundary)

The package depends on contracts, not on concrete persistence.

Public storage contract:

- `Sirix\Mezzio\Rbac\Contract\PermissionStoreInterface`

Read-only lookup contract used by authorization internals:

- `Sirix\Mezzio\Rbac\Contract\PermissionLookupInterface`

Default implementation:

- `Sirix\Mezzio\Rbac\InMemoryPermissionStore`

Later adapters can replace storage without changing the guard API.

Extensibility
-------------

[](#extensibility)

### Custom non-request actor provider

[](#custom-non-request-actor-provider)

Use `ActorProviderInterface` for non-request authorization through `GuardInterface`:

```
use Sirix\Mezzio\Rbac\Actor\Actor;
use Sirix\Mezzio\Rbac\Contract\ActorInterface;
use Sirix\Mezzio\Rbac\Contract\ActorProviderInterface;

final readonly class MyActorProvider implements ActorProviderInterface
{
    public function __construct(private MyAuthService $auth) {}

    public function getActor(): ActorInterface
    {
        $user = $this->auth->getIdentity();

        return new Actor($user?->getRoles() ?? ['guest']);
    }
}
```

### Custom request actor provider

[](#custom-request-actor-provider)

Use `RequestActorProviderInterface` for HTTP authorization through `RequestGuardInterface` / `AuthorizeMiddleware`:

```
use Psr\Http\Message\ServerRequestInterface;
use Sirix\Mezzio\Rbac\Actor\Actor;
use Sirix\Mezzio\Rbac\Contract\ActorInterface;
use Sirix\Mezzio\Rbac\Contract\RequestActorProviderInterface;

final readonly class MyRequestActorProvider implements RequestActorProviderInterface
{
    public function getActor(ServerRequestInterface $request): ActorInterface
    {
        $user = $request->getAttribute('user');

        return new Actor($user?->roles() ?? ['guest']);
    }
}
```

Register it in your dependencies:

```
'dependencies' => [
    'factories' => [
        RequestActorProviderInterface::class => MyRequestActorProviderFactory::class,
    ],
],
```

### Custom Permission Store

[](#custom-permission-store)

Implement `PermissionStoreInterface` to load permissions from a database, cache, or another source:

```
use Sirix\Mezzio\Rbac\Contract\PermissionAssociationInterface;
use Sirix\Mezzio\Rbac\Contract\PermissionStoreInterface;

final readonly class DatabasePermissionStore implements PermissionStoreInterface
{
    public function associationsForRole(string $role): array
    {
        // Fetch from DB and map to PermissionAssociation objects.
    }

    // ... implement other methods
}
```

### Custom Rules

[](#custom-rules)

As shown in the [Rules](#rules) section, implement `RuleInterface` to add dynamic logic to permissions. Rules are resolved through `RuleResolver`, which can use the PSR-11 container or instantiate rule classes directly.

Main Components
---------------

[](#main-components)

- `GuardInterface` / `Guard`
- `RequestGuardInterface` / `RequestGuard`
- `ActorProviderInterface`
- `RequestActorProviderInterface`
- `RequestAttributeActorProvider`
- `Permissions`
- `PermissionLookupInterface`
- `PermissionMatcher`
- `RuleResolver`
- `InMemoryPermissionStore`
- `AuthorizeMiddleware`
- `RbacAttribute`
- `#[Can(...)]`

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance88

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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 ~6 days

Total

5

Last Release

52d ago

Major Versions

0.1.2 → 1.0.02026-05-11

### Community

Maintainers

![](https://www.gravatar.com/avatar/6ecccf9003c061847e877eeea3bdf1b382f6f9dbb11d33112d6b2740bf0533f9?d=identicon)[sirix777](/maintainers/sirix777)

---

Top Contributors

[![sirix777](https://avatars.githubusercontent.com/u/68593154?v=4)](https://github.com/sirix777 "sirix777 (1 commits)")

---

Tags

middlewarelaminaspsr-15authorizationpermissionsrbacmezzioattributes

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/sirix-mezzio-rbac/health.svg)

```
[![Health](https://phpackages.com/badges/sirix-mezzio-rbac/health.svg)](https://phpackages.com/packages/sirix-mezzio-rbac)
```

###  Alternatives

[mezzio/mezzio

PSR-15 Middleware Microframework

3923.8M126](/packages/mezzio-mezzio)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k16](/packages/tempest-framework)[mezzio/mezzio-authentication-oauth2

OAuth2 (server) authentication middleware for Mezzio and PSR-7 applications.

28591.3k3](/packages/mezzio-mezzio-authentication-oauth2)[sunrise/http-router

A powerful solution as the foundation of your project.

17451.8k11](/packages/sunrise-http-router)[mezzio/mezzio-authentication

Authentication middleware for Mezzio and PSR-7 applications

131.7M39](/packages/mezzio-mezzio-authentication)

PHPackages © 2026

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