PHPackages                             langsys/laravel-access-guard - 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. langsys/laravel-access-guard

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

langsys/laravel-access-guard
============================

Entity-scoped RBAC for Laravel: users hold roles within entities (orgs, projects, teams), API keys are first-class authorizable subjects, with super-admin bypass and collection filtering.

v0.1.0(1mo ago)00MITPHPPHP ^8.2

Since Jun 12Pushed 1mo agoCompare

[ Source](https://github.com/langsys/laravel-access-guard)[ Packagist](https://packagist.org/packages/langsys/laravel-access-guard)[ RSS](/packages/langsys-laravel-access-guard/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (6)Versions (2)Used By (0)

Laravel Access Guard
====================

[](#laravel-access-guard)

Entity-scoped role-based authorization for Laravel. Where [`spatie/laravel-permission`](https://github.com/spatie/laravel-permission)gives users **global** roles, Access Guard scopes a role to an **entity** — a user is an *admin of this organization* and a *viewer of that project* — and treats **API keys as first-class authorizable subjects** alongside users.

It authorizes against **interfaces you implement on your own models**, so it works for any hierarchy (orgs, projects, teams, workspaces, tenants). No hard dependency on any other package; pairs naturally with [`langsys/laravel-api-keys`](https://github.com/langsys/laravel-api-keys).

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

[](#installation)

```
composer require langsys/laravel-access-guard
php artisan vendor:publish --tag=access-guard-migrations
php artisan vendor:publish --tag=access-guard-config   # optional
php artisan migrate
```

Migrations are guarded with `Schema::hasTable()`, so publishing into an app that already has `roles` / `permissions` tables is a safe no-op.

The model
---------

[](#the-model)

Authorization answers one question: **may this subject perform this permission on this entity?** A subject is either a user (with a role *in* the entity) or an API key (linked *to* the entity). Permissions are plain strings.

```
use Langsys\AccessGuard\Facades\AccessGuard;

AccessGuard::authorize('edit_projects', $project); // throws AuthorizationException if denied

if (AccessGuard::allows('view_projects', $project)) { /* ... */ }

// Keep only the entities the current subject may see:
$visible = AccessGuard::filterByPermission('view_projects', $projects);
```

Wiring your models
------------------

[](#wiring-your-models)

Implement the contracts on your own models — Access Guard never assumes your schema.

> Don't have a role-assignment scheme yet? Use the `HasRolesInEntities` trait (see *Assigning roles* below) and skip writing `userRoleInEntity()` by hand.

**User** (`AuthorizableByUser`, plus optional `Authorizable` for super admins):

```
use Langsys\AccessGuard\Contracts\Authorizable;
use Langsys\AccessGuard\Contracts\AuthorizableByUser;

class User extends Authenticatable implements Authorizable, AuthorizableByUser
{
    public function isSuperAdmin(): bool
    {
        return $this->type === UserType::SuperAdmin;
    }

    public function userRoleInEntity(mixed $entity): ?object
    {
        return $this->roleInEntity($entity); // your pivot lookup → a Role
    }

    public function roleHasPermission(object $role, string $permission): bool
    {
        return $role->hasPermission($permission);
    }

    public function userHasDisabledEntity(mixed $entity): bool
    {
        return $this->disabledEntities()->whereKey($entity->getKey())->exists();
    }
}
```

**Entity** — mark anything you authorize against with `GuardableResource`:

```
use Langsys\AccessGuard\Contracts\GuardableResource;

class Project extends Model implements GuardableResource {}
```

**API key** (optional, `AuthorizableByKey`) — see the api-keys integration below.

Roles &amp; permissions
-----------------------

[](#roles--permissions)

The bundled `Role` and `Permission` models (UUID keys, `value` + `label`) cover the vocabulary; `role_has_permissions` links them.

```
use Langsys\AccessGuard\Models\Role;

$admin = Role::create(['value' => 'project_admin', 'label' => 'Project Admin']);
$admin->grantPermissions(['view_projects', 'edit_projects']); // creates missing permissions
$admin->hasPermission('view_projects'); // true
```

Seed these from a seeder; override the models via `config('access-guard.models')`.

How the subject is resolved
---------------------------

[](#how-the-subject-is-resolved)

On each `authorize()` call, Access Guard:

1. checks for a super admin (the resolved user implements `Authorizable` and `isSuperAdmin()` is true) → allow;
2. otherwise picks the **API key** if one is present on the request, else the **authenticated user**;
3. runs the key path (`keyHasPermission` **and** `keyBelongsToEntity`) or the user path (`userRoleInEntity` → `roleHasPermission`, and not `userHasDisabledEntity`).

By default the user comes from `Auth::user()` and the API key from the `api_key` request attribute (set by `langsys/laravel-api-keys`). Override either from a service provider:

```
AccessGuard::resolveUserUsing(fn () => /* ... */);
AccessGuard::resolveApiKeyUsing(fn () => /* ... */);
```

Using with laravel-api-keys (zero-config)
-----------------------------------------

[](#using-with-laravel-api-keys-zero-config)

Install both packages and it just works — no subclassing, no contract to implement. When a key from `langsys/laravel-api-keys` authenticates, its middleware puts it on the request and Access Guard adapts it automatically: the key's own permissions are checked, and it is authorized against an entity only if it has been linked to that entity.

Link keys to entities with the `AuthorizesWithApiKeys` trait:

```
use Langsys\AccessGuard\Concerns\AuthorizesWithApiKeys;
use Langsys\AccessGuard\Contracts\GuardableResource;

class Project extends Model implements GuardableResource
{
    use AuthorizesWithApiKeys;
}

$project->grantApiKey($apiKey);   // this key may now act on this project
$project->revokeApiKey($apiKey);
```

`AccessGuard::authorize('edit_projects', $project)` then passes for a key that both holds `edit_projects` and is linked to `$project`, and is denied otherwise. Linking is the one explicit step — it's a security boundary — and it lives in the `entity_has_api_keys` pivot. Without api-keys installed, only the user path runs.

**Other key systems:** any object implementing `AuthorizableByKey` is used as-is. Point `config('access-guard.api_key.bridge')` at a different key class to auto-adapt it, or set it to `null` to turn the bridge off.

Assigning roles (batteries included)
------------------------------------

[](#assigning-roles-batteries-included)

If you don't already have a membership scheme, add the `HasRolesInEntities` trait to your subject and implement `AuthorizableInEntity`. You get entity-scoped assignment backed by the `model_has_roles` pivot — no custom pivot or `userRoleInEntity()` to write:

```
use Langsys\AccessGuard\Concerns\HasRolesInEntities;
use Langsys\AccessGuard\Contracts\AuthorizableInEntity;

class User extends Authenticatable implements Authorizable, AuthorizableInEntity
{
    use HasRolesInEntities;
}
```

```
$user->assignRole('project_admin', $project);   // role value, backed enum, or Role model
$user->syncRoles(['viewer'], $project);
$user->removeRole('project_admin', $project);

$user->hasRole('project_admin', $project);                // bool
$user->hasPermissionInEntity('edit_projects', $project);  // unions every role in the entity
$user->rolesInEntity($project);                           // Collection
$user->permissionsInEntity($project);                     // array
```

A subject can hold multiple roles in one entity; permission checks union them. Override `entityIsDisabled($entity)` to exclude a subject from an entity even when a role would grant access (e.g. a user who left a project).

> Already have your own pivot (like langsys's `organization_has_users.role`)? Skip the trait and implement `AuthorizableByUser` instead — both paths are supported.

Gate &amp; policy integration
-----------------------------

[](#gate--policy-integration)

With `register_gate` on (default), Gate checks against a `GuardableResource` route through Access Guard, so the idiomatic Laravel APIs just work:

```
$user->can('edit_projects', $project);
$this->authorize('edit_projects', $project);   // in a controller
// @can('edit_projects', $project) ... @endcan
```

A subject whose `isSuperAdmin()` is true passes every Gate check (`super_admin_via_gate`). Checks that aren't against a `GuardableResource` are left untouched for your own gates and policies.

Route middleware
----------------

[](#route-middleware)

```
Route::get('/projects/{project}', [ProjectController::class, 'show'])
    ->middleware('access-guard:view_projects,project');
```

The first argument is the permission; the optional second names the route parameter holding the entity (otherwise the first route-bound `GuardableResource`is used). Denial throws `AuthorizationException` (403).

Caching
-------

[](#caching)

The role → permission map is cached (config `cache.store` / `expiration_time`) and flushed automatically on grant/revoke and any role/permission save or delete. Flush manually with `php artisan access-guard:cache-reset`.

Artisan commands
----------------

[](#artisan-commands)

```
php artisan access-guard:create-permission view_projects "View projects"
php artisan access-guard:create-role project_admin "Project Admin" --permissions=view_projects,edit_projects
php artisan access-guard:show          # roles and the permissions they grant
php artisan access-guard:cache-reset
```

Enums
-----

[](#enums)

Anywhere a permission or role name is accepted you can pass a string **or** a backed enum:

```
enum Ability: string { case ViewProjects = 'view_projects'; }

$role->grantPermissions([Ability::ViewProjects]);
AccessGuard::allows(Ability::ViewProjects, $project);
```

Events
------

[](#events)

Set `events_enabled => true` to fire `RoleAssignedToModel`, `RoleRemovedFromModel`, `PermissionAssignedToRole`, and `PermissionRemovedFromRole` — useful for audit logging.

Testing
-------

[](#testing)

```
composer install
composer test
```

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance91

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity36

Early-stage or recently created project

 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

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7b51636a402c8fc758f251e1a92b468a3515a5038a3c278a4499782688b83586?d=identicon)[hcuadra](/maintainers/hcuadra)

---

Top Contributors

[![hcuadra811](https://avatars.githubusercontent.com/u/7507416?v=4)](https://github.com/hcuadra811 "hcuadra811 (3 commits)")

---

Tags

laravelauthorizationrolespermissionsrbacmulti-tenantaccess-control

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/langsys-laravel-access-guard/health.svg)

```
[![Health](https://phpackages.com/badges/langsys-laravel-access-guard/health.svg)](https://phpackages.com/packages/langsys-laravel-access-guard)
```

###  Alternatives

[hasinhayder/tyro

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

6765.1k6](/packages/hasinhayder-tyro)[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k102.4M1.5k](/packages/spatie-laravel-permission)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M348](/packages/psalm-plugin-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M137](/packages/laravel-pulse)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

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