PHPackages                             akindutire/authorization-pkg - 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. [Database &amp; ORM](/categories/database)
4. /
5. akindutire/authorization-pkg

ActiveLibrary[Database &amp; ORM](/categories/database)

akindutire/authorization-pkg
============================

A modern, attribute-based authorization package for Laravel 9+ that works with any Eloquent model using PHP 8 attributes

v1.0.0(1mo ago)20MITPHPPHP ^8.1|^8.2|^8.3CI passing

Since Jun 3Pushed 1mo agoCompare

[ Source](https://github.com/akindutire/authorization-pkg)[ Packagist](https://packagist.org/packages/akindutire/authorization-pkg)[ RSS](/packages/akindutire-authorization-pkg/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (7)Versions (3)Used By (0)

Laravel Model-Agnostic Authorization
====================================

[](#laravel-model-agnostic-authorization)

A modern, attribute-based authorization package for Laravel 9+ that works with **any** Eloquent model using PHP 8 attributes. Unlike traditional user-centric permission systems, this package lets you attach abilities directly to any model - Articles, Organizations, TeamMembers, or any entity in your application.

**Built for scale** - optimized for applications with millions of entities.

[![Latest Version](https://camo.githubusercontent.com/cfcfb63f58d65e05b7afd178fe4d7d44ca3e3504d9abf6b7aa4a98bbdb38b6ba/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616b696e6475746972652f617574686f72697a6174696f6e2d706b672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/akindutire/authorization-pkg)[![Total Downloads](https://camo.githubusercontent.com/e1fccfa96a510a77bb9f3022709765860947cfe36d9a693bbcf4d51d717df1f0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616b696e6475746972652f617574686f72697a6174696f6e2d706b672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/akindutire/authorization-pkg)[![License](https://camo.githubusercontent.com/530467b0d66b570a9c405ebd7e9743c4daec77d4376d84eccbb3e82fe1128102/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f616b696e6475746972652f617574686f72697a6174696f6e2d706b672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/akindutire/authorization-pkg)

Why Model-Agnostic?
-------------------

[](#why-model-agnostic)

Traditional systems: `User` → has Permissions → to perform Actions → on Resources

This package: `Subject` (any model) → has Abilities → validated via Attributes

```
// ❌ Traditional: User-centric - doesn't model entity-specific abilities
$user->hasPermission('edit-article');

// ✅ This package: Model-agnostic - abilities belong to entities
#[HasAny(['can_edit'], Article::class, 'id')]
public function update(int $articleId) {
    // The ARTICLE itself has 'can_edit' ability
    // The entity being modified controls who can modify it
}
```

Key Concepts
------------

[](#key-concepts)

- **Subject**: Any Eloquent model with the `HasPermissions` trait (User, Article, TeamMember, Organization, etc.)
- **Abilities**: Actions a subject can perform (`can_edit`, `can_publish`, `can_invite`)
- **Attributes**: PHP 8 attributes declaring ability requirements (`#[HasAny]`, `#[HasAll]`)
- **Validation**: Automatic checking via `ValidateSubjectAction` middleware

### How It Works: Architecture Flow

[](#how-it-works-architecture-flow)

```
Controller Method
    ↓
[Attribute Declaration]
#[HasAny(['can_edit'], Article::class, 'id')]
    ↓
[Middleware: ValidateSubjectAction]
    ↓
[Subject Resolution]
- Lookup model by property: Article::where('id', $value)
- Extract abilities from database columns
    ↓
[Permission Service]
- Check if subject has required abilities
- Respect revoked abilities
    ↓
[Cache Result]
- Multi-layer caching for performance
    ↓
[Allow/Deny Request]

```

### Terminology: Why "Abilities"?

[](#terminology-why-abilities)

**In code**: The package uses `permissions` (columns/methods) for backward compatibility with Laravel conventions.

**In concept**: We call these **abilities** because:

- **Model-agnostic**: "Article has ability to be edited" (not "Article has permission")
- **Subject-focused**: Abilities belong to the entity being authorized
- **Industry standard**: CanCanCan (Rails) and CASL (JavaScript) use "abilities"
- **Clear semantics**: Describes what subjects *can do*

AlternativeWhy Not?CapabilitiesConflicts with PHP extensionsGrantsSounds temporaryActionsToo generic, conflicts with controllersPermissionsImplies user-centric (kept in code for compatibility)Features
--------

[](#features)

- 🎯 **Model-agnostic** - Works with any Eloquent model, not just Users
- 🏷️ **Attribute-based** - Declarative authorization using PHP 8 attributes
- ⚡ **Auto-invalidating cache** - Detects attribute changes without manual clearing
- 🔍 **Flexible resolution** - Lookup subjects by any property (id, uuid, email, slug)
- 🎭 **Revocation support** - Explicitly deny abilities that override grants
- 🎨 **Facade included** - Easy-to-use facade for programmatic checks
- 🔒 **Middleware validation** - Automatic checks using reflection
- 🚀 **Production-ready** - Multi-layer caching, optimized for 500M+ entities
- ⚛️ **Race condition safe** - Atomic updates using database operations

Performance Highlights
----------------------

[](#performance-highlights)

- **8-25ms** permission check latency (p50) at scale
- **98% reduction** in database queries via intelligent caching
- **99%+ cache hit rate** in production environments
- **Zero runtime reflection overhead** via metadata caching
- **Horizontal scalability** - tested with 500M entities

📊 See [SCALABILITY.md](SCALABILITY.md) for detailed benchmarks.

Quick Start
-----------

[](#quick-start)

### 1. Install

[](#1-install)

```
composer require akindutire/authorization-pkg
```

### 2. Add Abilities to Any Model

[](#2-add-abilities-to-any-model)

```
use Akindutire\Authorization\Traits\HasPermissions;

// Works with ANY Eloquent model
class Article extends Model
{
    use HasPermissions;

    protected $casts = [
        'allowed_permissions' => 'array',  // What this article can do
        'revoked_permissions' => 'array',  // Explicitly denied
    ];
}

class TeamMember extends Model
{
    use HasPermissions;

    protected $casts = [
        'allowed_permissions' => 'array',  // What this member can do
        'revoked_permissions' => 'array',
    ];
}

class Organization extends Model
{
    use HasPermissions;

    protected $casts = [
        'allowed_permissions' => 'array',  // What this org can do
        'revoked_permissions' => 'array',
    ];
}
```

### 3. Grant Abilities

[](#3-grant-abilities)

```
// Articles have publication abilities
$article = Article::find(1);
$article->grantPermission(['can_be_edited', 'can_be_published']);

// Team members have role-based abilities
$member = TeamMember::find(1);
$member->grantPermission(['can_invite', 'can_manage_billing']);

// Organizations have feature abilities
$org = Organization::find(1);
$org->grantPermission(['can_use_api', 'can_white_label']);
```

### 4. Protect Controller Methods with Attributes

[](#4-protect-controller-methods-with-attributes)

```
use Akindutire\Authorization\Attributes\{HasAny, HasAll};
use Akindutire\Authorization\Attributes\SubjectValue;

class ArticleController
{
    // Check if Article has 'can_be_edited' ability
    #[HasAny(['can_be_edited'], Article::class, 'id')]
    public function update(
        #[SubjectValue] int $id,
        Request $request
    ) {
        // Only executes if the Article can be edited
        $article = Article::find($id);
        $article->update($request->all());
    }

    // Article must have BOTH abilities
    #[HasAll(['can_be_published', 'can_be_featured'], Article::class, 'id')]
    public function publish(#[SubjectValue] int $id) {
        Article::find($id)->update(['published_at' => now()]);
    }

    // Works with any property, not just 'id'
    #[HasAny(['can_be_viewed'], Article::class, 'slug')]
    public function show(#[SubjectValue] string $slug) {
        return Article::where('slug', $slug)->firstOrFail();
    }
}

class TeamController
{
    // Check TeamMember abilities
    #[HasAny(['can_invite'], TeamMember::class, 'member_id')]
    public function invite(
        #[SubjectValue('member_id')] Request $request
    ) {
        // TeamMember must have 'can_invite' ability
    }
}
```

### 5. Register Middleware

[](#5-register-middleware)

```
// app/Http/Kernel.php
protected $middlewareAliases = [
    'validate.subject.action' => \Akindutire\Authorization\Middleware\ValidateSubjectAction::class,
];

// routes/web.php
Route::middleware(['validate.subject.action'])->group(function () {
    Route::put('/articles/{id}', [ArticleController::class, 'update']);
    Route::post('/articles/{id}/publish', [ArticleController::class, 'publish']);
});
```

Use Cases
---------

[](#use-cases)

### 1. Content Management Systems

[](#1-content-management-systems)

```
// Articles control their own editability
$article->grantPermission(['can_be_edited', 'can_be_deleted']);
$article->revokePermission(['can_be_deleted']); // Make read-only

#[HasAny(['can_be_edited'], Article::class, 'id')]
public function update(int $id) { }
```

### 2. Multi-Tenant SaaS

[](#2-multi-tenant-saas)

```
// Organizations have feature abilities
$org->grantPermission(['can_use_api', 'can_export_data', 'can_white_label']);

#[HasAll(['can_use_api'], Organization::class, 'org_id')]
public function apiAccess(#[SubjectValue] int $org_id) { }
```

### 3. Team Collaboration

[](#3-team-collaboration)

```
// Team members have role-based abilities
$member->grantPermission(['can_invite', 'can_view_analytics', 'can_manage_billing']);

#[HasAny(['can_invite', 'can_manage_team'], TeamMember::class, 'id')]
public function addMember(#[SubjectValue] int $id) { }
```

### 4. Resource Sharing

[](#4-resource-sharing)

```
// Documents have sharing abilities
$document->grantPermission(['can_be_shared', 'can_be_commented']);

#[HasAny(['can_be_shared'], Document::class, 'uuid')]
public function share(#[SubjectValue] string $uuid) { }
```

Core API
--------

[](#core-api)

### Granting and Revoking Abilities

[](#granting-and-revoking-abilities)

```
// Grant abilities (single or multiple)
$subject->grantPermission('can_edit');
$subject->grantPermission(['can_edit', 'can_delete']);

// Revoke abilities (explicitly deny)
$subject->revokePermission('can_delete');
$subject->revokePermission(['can_delete', 'can_admin']);

// Check abilities
$subject->hasPermission('can_edit');              // Check single
$subject->hasAnyPermission(['can_edit', 'can_view']); // Has any
$subject->hasAllPermissions(['can_edit', 'can_publish']); // Has all

// Get abilities
$allowed = $subject->getAllowedPermissions();     // ['can_edit', 'can_view']
$revoked = $subject->getRevokedPermissions();     // ['can_delete']
$effective = $subject->getEffectivePermissions(); // Allowed minus revoked
```

### Facade Usage

[](#facade-usage)

```
use Akindutire\Authorization\Facades\EntityPermission;

// Check if subject has abilities
$canEdit = EntityPermission::subject($article)->hasAny(['can_be_edited']);
$canPublish = EntityPermission::subject($article)->hasAll(['can_be_published', 'can_be_featured']);

// Get role-based ability templates from config
$ownerAbilities = EntityPermission::getAbilities('owner');
```

### Attribute Options

[](#attribute-options)

```
// Basic usage
#[HasAny(['can_edit'], Article::class, 'id')]

// Custom property resolution
#[HasAny(['can_edit'], Article::class, 'uuid')]
#[HasAny(['can_edit'], Article::class, 'slug')]

// Extract subject value from request
#[HasAny(['can_edit'], Article::class, 'id')]
public function update(
    #[SubjectValue('article_id')] Request $request
) { }

// Require all abilities
#[HasAll(['can_publish', 'can_feature'], Article::class, 'id')]
```

Configuration
-------------

[](#configuration)

### Ability Templates (Optional)

[](#ability-templates-optional)

Define reusable ability templates in config:

```
// config/akindutire-authorization.php
'abilities' => [
    'article_editor' => ['can_be_edited', 'can_be_deleted'],
    'article_publisher' => ['can_be_edited', 'can_be_published', 'can_be_featured'],
    'team_admin' => ['can_invite', 'can_remove', 'can_manage_billing'],
    'team_member' => ['can_invite', 'can_view_analytics'],
],
```

Then use them:

```
$article->grantPermission(EntityPermission::getAbilities('article_publisher'));
$member->grantPermission(EntityPermission::getAbilities('team_admin'));
```

### Custom Column Names

[](#custom-column-names)

```
'column_names' => [
    'allowed_permissions' => 'abilities',        // Rename columns
    'revoked_permissions' => 'denied_abilities',
],
```

### Cache Configuration

[](#cache-configuration)

```
'entity_cache_ttl' => 300,  // 5 minutes
'reflection_cache_enabled' => true,
'auto_invalidate_reflection_cache' => true,
```

Database Setup
--------------

[](#database-setup)

### Generate Migrations

[](#generate-migrations)

```
php artisan make:permission-migration articles
php artisan make:permission-migration team_members
php artisan make:permission-migration organizations
```

This creates:

```
Schema::table('articles', function (Blueprint $table) {
    $table->json('allowed_permissions')->nullable();
    $table->json('revoked_permissions')->nullable();
    // Database-specific indexes for optimal performance
});
```

### Run Migrations

[](#run-migrations)

```
php artisan migrate
```

Advanced Examples
-----------------

[](#advanced-examples)

### Complex Controller

[](#complex-controller)

```
class ArticleController
{
    #[HasAny(['can_be_edited'], Article::class, 'id')]
    public function update(#[SubjectValue] int $id, Request $request)
    {
        $article = Article::find($id);
        $article->update($request->validated());
        return response()->json($article);
    }

    #[HasAll(['can_be_published', 'can_be_featured'], Article::class, 'id')]
    public function publishAndFeature(#[SubjectValue] int $id)
    {
        $article = Article::find($id);
        $article->update([
            'published_at' => now(),
            'featured' => true,
        ]);
        return response()->json($article);
    }

    #[HasAny(['can_be_viewed'], Article::class, 'slug')]
    public function showBySlug(#[SubjectValue] string $slug)
    {
        return Article::where('slug', $slug)->firstOrFail();
    }
}
```

### Dynamic Ability Management

[](#dynamic-ability-management)

```
// Grant abilities based on business logic
if ($user->isAdmin()) {
    $article->grantPermission(['can_be_edited', 'can_be_deleted', 'can_be_published']);
} elseif ($user->isEditor()) {
    $article->grantPermission(['can_be_edited']);
}

// Temporarily revoke abilities
$article->revokePermission(['can_be_deleted']); // Lock article

// Later restore
$article->grantPermission(['can_be_deleted']); // Removes from revoked
```

### Multi-Property Resolution

[](#multi-property-resolution)

```
// By ID
#[HasAny(['can_edit'], User::class, 'id')]
public function updateById(#[SubjectValue] int $id) { }

// By UUID
#[HasAny(['can_edit'], User::class, 'uuid')]
public function updateByUuid(#[SubjectValue] string $uuid) { }

// By email
#[HasAny(['can_login'], User::class, 'email')]
public function authenticate(#[SubjectValue] string $email) { }
```

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- Laravel 9.0 or higher
- Redis or Memcached (recommended for production)

Documentation
-------------

[](#documentation)

- 📖 [Full Documentation](DOCUMENTATION.md)
- 📈 [Scalability Guide](SCALABILITY.md)
- 🔌 [API Reference](docs/api.html)
- ⚡ [Quick Start](docs/quickstart.html)

Why This Package?
-----------------

[](#why-this-package)

Traditional SystemsThis PackageUser-centricModel-agnosticPermissions on usersAbilities on any model`$user->can('edit-post')``#[HasAny(['can_edit'], Article::class)]`Doesn't scale to entity-specific rulesEntity owns its abilitiesComplex policy classesDeclarative attributesComparison
----------

[](#comparison)

FeatureSpatie PermissionLaravel GatesThis PackageModel-agnostic❌ User-only❌ User-only✅ Any modelAttribute-based❌❌✅Auto-caching⚠️ Manual❌✅ Auto-invalidatingFlexible resolution❌ ID only❌✅ Any propertyRevocation support❌⚠️ Limited✅ NativeScale (entities)&lt; 1MN/A500M+ testedContributing
------------

[](#contributing)

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Akindutire Ayomide](https://github.com/akindutire)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance90

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity50

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

Unknown

Total

1

Last Release

51d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5477c635ffd39fa69f401137b05683a43b3293389eff1d361c0bb21c635ca801?d=identicon)[akindutire](/maintainers/akindutire)

---

Top Contributors

[![akindutire](https://avatars.githubusercontent.com/u/40896417?v=4)](https://github.com/akindutire "akindutire (41 commits)")

---

Tags

laraveleloquentauthorizationrolespermissionsattributesphp8

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[hasinhayder/tyro

Tyro - The ultimate Authentication, Authorization, and Role &amp; Privilege Management solution for Laravel 12 &amp; 13

6765.1k6](/packages/hasinhayder-tyro)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

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