waventra/laravel-identity

Laravel identity package for users, teams, roles, and permissions, with JWT authentication via waventra/laravel-jwt-token.

Maintainers

Package info

github.com/waventra/laravel-identity

pkg:composer/waventra/laravel-identity

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-09-01 04:31 UTC

This package is auto-updated.

Last update: 2026-09-01 04:39:17 UTC


README

Stable version 1.0.0.

Laravel identity package for users, teams, roles, and permissions, with authentication through waventra/laravel-jwt-token. Independent of any host application.

Attach HasIdentity to your user model. Login, refresh, and logout are issued as JWT access and rotating refresh tokens. Roles, permissions, and teams sit on top of that user.

Install: composer require waventra/laravel-identity:^1.0
Packagist: packagist.org/packages/waventra/laravel-identity
Source: github.com/waventra/laravel-identity

Requirements

  • PHP 8.2+
  • Laravel 10, 11, or 12
  • waventra/laravel-jwt-token ^1.0 (pulled in automatically)

Installation

composer require waventra/laravel-identity:^1.0

Laravel auto-discovers the service provider and Identity facade.

php artisan vendor:publish --tag=identity-config
php artisan migrate

Migrations load automatically. Publish them only if you want copies in your app.

Setup

Add the trait to your authenticatable model:

use Illuminate\Foundation\Auth\User as Authenticatable;
use Waventra\Identity\Traits\HasIdentity;
use Waventra\Jwt\Contracts\JwtSubject;

class User extends Authenticatable implements JwtSubject
{
    use HasIdentity;
}

HasIdentity includes JWT helpers (issueJwt(), revokeAllJwtTokens()) and custom claims for email, current team, roles, and permissions.

Point the package at that model in config/identity.php (or IDENTITY_USER_MODEL):

'user' => [
    'model' => App\Models\User::class,
    'table' => 'users',
],

Register the JWT guard in config/auth.php if Identity did not already add it:

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

Generate JWT keys (RS256) or a secret (HS256) as documented in waventra/laravel-jwt-token:

php artisan jwt:generate-keys

Authentication

Identity registers these routes (disable with identity.auth.routes = false):

Method Path Auth
POST /api/auth/login Public
POST /api/auth/refresh Refresh token
GET /api/auth/me jwt.auth
POST /api/auth/logout jwt.auth
POST /api/auth/logout-all jwt.auth
curl -X POST http://localhost:8000/api/auth/login \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{\"email\":\"ada@example.com\",\"password\":\"secret\"}"

The response is a JWT token pair plus the identity user:

{
    "access_token": "eyJ...",
    "token_type": "Bearer",
    "expires_in": 900,
    "refresh_expires_in": 1209600,
    "refresh_token": "selector.verifier",
    "user": {
        "id": 1,
        "email": "ada@example.com",
        "roles": ["admin"],
        "permissions": ["users.create"]
    }
}
use Waventra\Identity\Facades\Identity;

$pair = Identity::attempt(['email' => $email, 'password' => $password], $request);
$pair = Identity::issue($user, $request);
Identity::refresh($refreshToken, $request);
Identity::logout($request->bearerToken(), $refreshToken, $request);
Identity::logoutAll($user);

Protect routes with the JWT middleware from laravel-jwt-token, then identity roles:

Route::middleware(['jwt.auth', 'role:admin'])->get('/admin', ...);
Route::middleware(['jwt.auth', 'permission:users.create'])->post('/users', ...);

Roles and permissions

use Waventra\Identity\Facades\Identity;

Identity::createRole('admin');
Identity::createPermission('users.create');

$role = Identity::findRole('admin');
$role->givePermissionTo('users.create');

$user->assignRole('admin');
$user->givePermissionTo('users.delete');

$user->hasRole('admin');
$user->hasPermissionTo('users.create');
$user->hasAnyRole(['admin', 'editor']);
$user->hasAllPermissions(['users.create', 'users.delete']);

posts.* matches posts.edit. * matches every permission. Users with the super-admin role skip permission checks.

Laravel Gates are wired automatically for permissions that exist:

$user->can('users.create');

Middleware

Route::get('/users', ...)->middleware('role:admin');
Route::post('/users', ...)->middleware('permission:users.create');
Route::get('/settings', ...)->middleware('role_or_permission:admin|settings.edit');

Pipe-separated names mean any.

Blade

@role('admin')
    ...
@endrole

@permission('users.create')
    ...
@endpermission

@teamrole('owner')
    ...
@endteamrole

Teams

$team = $user->createTeam('Acme');

$team->addMember($other, 'admin');
$user->switchTeam($team);
$user->belongsToTeam($team);
$user->isOwnerOf($team);
$user->hasTeamRole($team, ['owner', 'admin']);

$invitation = $user->inviteToTeam($team, 'jane@example.com', 'member');
$jane->acceptTeamInvitation($invitation->token);

Roles and permissions can be global or limited to a team:

$user->assignRole('editor', $team);
$user->hasRole('editor', $team);

Identity::setTeam($team);
$user->hasPermissionTo('posts.publish');
Identity::forgetTeam();

When a current team is set (or Identity::setTeam() is used), checks use that team. Global roles still apply inside a team when identity.teams.include_global is true.

Artisan

Command Purpose
identity:create-role {name} Create a role
identity:create-permission {name} Create a permission
identity:assign-role {email} {role} Assign a role (--team= optional)
identity:create-team {name} --owner= Create a team
identity:super-admin {email} Create or promote a super-admin
identity:cache-reset Flush the identity cache

Configuration

Key Default Purpose
defaults.guard web Guard stored on roles and permissions
super_admin_role super-admin Role that bypasses permission checks (null to disable)
teams.enabled true Team membership and team-scoped roles
teams.include_global true Global roles apply inside a team
wildcard true posts.* and * matching
cache.ttl 3600 Seconds (0 disables cache)
auth.routes true Register /api/auth/* JWT routes
auth.prefix api/auth Auth route prefix
auth.guard api Laravel guard using the jwt driver
auth.include_roles_in_token true Put roles and permissions in JWT claims

Tables are prefixed with identity_ so they do not collide with other packages.

Events

RoleAssigned, RoleRemoved, PermissionAssigned, TeamCreated, TeamMemberAdded, TeamMemberRemoved.

Path repository (local package)

{
    "repositories": [
        {
            "type": "path",
            "url": "../laravel-identity"
        }
    ]
}
composer require waventra/laravel-identity:@dev

Copyright and license

Copyright (c) 2026 Waventra. All rights reserved.

This software is free to use. You may not copy, modify, or sell it.

See LICENSE for the full terms.