Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Iam Laravel Package

nextdeveloper/iam

nextdeveloper/iam is a Laravel identity and access management package for managing users, roles, permissions, and authentication and authorization workflows. It aims to simplify access control setup, but has limited adoption and sparse documentation, so evaluate fit carefully.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and scaffold

composer require nextdeveloper/iam
php artisan vendor:publish --provider="Nextdeveloper\IAM\IAMServiceProvider"
php artisan migrate

Publish both the config and migrations in one pass if the package offers tagged publishing (--tag=iam-config, --tag=iam-migrations). Run migrations before you touch your users table so foreign keys resolve.

Where to look first

  1. config/iam.php — the guard model, table names, and any super-admin/owner bypass toggle. This file tells you more about the package's opinions than the README does.
  2. The published migrations — these reveal the actual data model (roles vs. permissions vs. groups/teams) and whether pivot tables are polymorphic. Polymorphic pivots mean you can attach roles to models other than User, which changes your whole design.
  3. The trait(s) shipped in src/Traits — this is the public surface you'll call from your app.
  4. The package's route files / HTTP controllers — as of v2.0.26 the package ships its own admin endpoints (QR sign-in badge issue/revoke). Find where those routes are registered and what middleware they carry before you let them load in production.

Wire up your User model

use Nextdeveloper\IAM\Traits\HasRoles; // verify exact namespace in src/Traits

class User extends Authenticatable
{
    use HasRoles;
}

First use case — protect an admin area

Seed two roles (admin, member) and a handful of permissions, assign admin to your own account in a seeder, then guard the route group:

Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin', AdminDashboard::class);
});

That single flow — seed role → assign → middleware → verify a non-admin gets 403 — is the fastest way to confirm the package is installed correctly and to learn its naming conventions before you build anything else. Only after that baseline is green should you go near the v2.0.26 QR badge endpoints, since they're an additional privileged surface layered on the same authorization model.

Implementation Patterns

Permission names as constants, not strings

Every IAM package degrades the moment permission strings drift between seeders, middleware, and Blade. Define them once:

enum Permission: string {
    case InvoicesView = 'invoices.view';
    case InvoicesEdit = 'invoices.edit';
}

Then reference Permission::InvoicesView->value in seeders and @can(...) in views. This costs ten minutes and saves you a week of 403s.

Seed idempotently

Use firstOrCreate / updateOrCreate for roles and permissions, never create. Role seeders get re-run during deploys and local resets; duplicates make assignRole behave unpredictably.

Separate the two authorization layers

  • Coarse — role middleware on route groups (role:admin). Cheap, readable, good for whole sections of an app.
  • Fine — Gates/Policies for individual actions (invoices.edit). Register policies the normal Laravel way and let them query the package:
public function update(User $user, Invoice $invoice): bool
{
    return $user->hasPermissionTo('invoices.edit')
        && $invoice->team_id === $user->current_team_id;
}

Don't cram tenancy scoping into role checks. Roles answer "what kind of user"; policies answer "may this user touch this record".

Blade and navigation

Gate UI on the same check the controller enforces, but derive nav items from permissions rather than hardcoding role names:

@can('invoices.view')
    <a href="{{ route('invoices.index') }}">Invoices</a>
@endcan

If your sidebar is built from a config array, add a permission key per item and filter the array in a view composer. One place to change when permissions evolve.

Assign on the event, not in the controller

New users should get their baseline role from a Registered listener or an observer, not from RegisterController. Keeps registration, invite flows, SSO provisioning, and artisan-created users all consistent.

QR sign-in badges (v2.0.26+)

The new admin endpoints issue and revoke a per-user QR badge. Treat the badge as a credential, not a profile field:

  • Don't call the package endpoints directly from your frontend. Proxy them through your own controller so the authorization check, the audit log, and any tenancy scoping live in your code, not in a vendor route you may not control the middleware of.
  • Gate issuance behind a permission, not just role:admin — @can('iam.badges.issue') — so you can delegate badge printing to HR without handing over the whole admin area.
  • Bind the badge lifecycle to the user lifecycle. Deactivated, Suspended, Deleted, and role-revocation events should all revoke outstanding badges. The one flow people forget is "user left the company, badge still opens the door."
  • Record issue and revoke. badge_id, issuer, timestamp, reason. If the package fires events on issue/revoke, subscribe; otherwise wrap the call in a small service that writes the audit row itself.
  • Keep the badge short-lived and rotatable. A printed/screenshot QR is a bearer token. Prefer a short TTL plus a re-issue flow over a permanent image in an email signature.

Testing

Build a small helper so tests read clearly:

protected function actingAsAdmin(): static
{
    return $this->actingAs(User::factory()->create())
        ->withRole('admin'); // or assign in the factory state
}

Add a factory state per role (->admin(), ->member()) and assert both the allow and the deny path. IAM bugs are almost always false-permits, and a suite that only tests happy paths won't catch them. With v2.0.26 in play, add three tests to the suite: non-admin cannot hit the issue endpoint, non-admin cannot hit the revoke endpoint, and a revoked badge no longer authenticates.

Admin surface

Resist building role management UI in v1. Ship an artisan command (iam:grant {email} {role}) plus a seeder, and defer CRUD screens until the permission set stabilizes. Role-editing UIs invite non-technical edits that silently break middleware assumptions. The same rule applies to badges: expose issue and revoke as two explicit buttons behind a permission before you build a badge management screen.

Wrap the new endpoints in a feature flag

Because v2.0.26 is a feature addition inside a patch-level release line, you may have upgraded without meaning to expose new routes. Guard your badge proxy behind a config flag (config('features.qr_badges')) so the surface can be turned off per environment while you finish the revocation wiring.

Gotchas and Tips

Cache invalidation is the #1 failure mode. Most packages of this shape cache a user's resolved permissions. After assigning or revoking a role in code, the change may not appear until the cache is flushed — and in long-running workers (Octane, queues) it may not appear until restart. Find the cache key or the forgetCachedPermissions()-style method in src/ and call it from your role-change service, not ad hoc from controllers. Wrap assignment in a single service class so the invalidation can never be forgotten.

Multiple guards. Sanctum, a separate admin guard, or API tokens each resolve permissions differently. Confirm in the config which guard the package inspects; if you have more than one, test explicitly that an API-token request sees the same permissions as a session request. This is where "works in the browser, 403 from the SPA" comes from — and it matters double for the v2.0.26 admin endpoints, which are API routes by nature.

The bundled admin endpoints are a route surface you didn't write. New in v2.0.26: issue/revoke badge endpoints. Verify (a) whether they auto-register on install, (b) their prefix, (c) what middleware they carry — auth alone is not auth + admin. If the package's own middleware assumptions don't match yours, don't patch vendor routes: disable route registration in config if supported, and re-expose the logic through a controller you own.

Revocation lag on badges. A revoked QR badge is a cache entry in someone else's system as much as yours. Check whether revoke is immediate (DB row deleted) or soft (flag flipped, checked at scan time). If it's soft, confirm the scanning path actually reads the flag; if it's cached anywhere, flush it in the same service call that revokes. Also confirm revocation is idempotent — offboarding hooks fire twice more often than you'd expect.

Super-admin bypass is a footgun. If there's an owner/super-admin short-circuit in a Gate before() callback, it will silently make every @can pass for that user — including in tests you wrote to verify denials. Know whether it's on, whether it respects Gate::forUser(), and whether it also short-circuits the new badge permissions. A super-admin who can silently mint badges is a compliance finding.

Permission naming collisions with Laravel. If the package registers a role middleware alias or a can-adjacent helper, check bootstrap/app.php/Kernel.php for conflicts with your own aliases. Alias collisions fail loudly at boot; helper function collisions fail silently at runtime.

Migration ordering with an existing user base. On a live app, adding role tables after users exist means you need a backfill migration or seeder that grants a default role to every existing row. Without it, day-one deploy locks everyone out. Ship the backfill in the same deploy as the middleware.

Version pinning and abstraction. This is a single-maintainer package with a very small install base and no downstream dependents — treat it as a vendored dependency, not infrastructure. Pin an exact version in composer.json, and put your own thin interface in front of it:

interface AccessControl {
    public function assign(string $role, User $user): void;
    public function allows(User $user, string $permission): bool;
    public function issueBadge(User $user): string;
    public function revokeBadge(User $user): void;
}

One adapter class implementing that interface means swapping to spatie/laravel-permission later is a day's work instead of a refactor across every controller, policy, and Blade file. The two badge methods are what stop the v2.0.26 endpoints from leaking into forty call sites — and if badge issuance later moves to a dedicated package or an internal service, you change one file. Given the package's stated philosophy — "make it as complicated as they want" — you will outgrow the defaults, and the abstraction is what lets you do so incrementally.

Check the release metadata. The listed last release date is in the future relative to normal release cadences, which usually signals a version-tagging quirk or a mirrored/automated publish. v2.0.26 shipping a user-facing feature under a patch version is another signal that the project's semver discipline is loose: a require: "nextdeveloper/iam": "^2.0" constraint can hand you new HTTP endpoints on a routine composer update. Pin exact versions, read the diff before upgrading, and check whether new routes were registered after every bump.

Extension points worth knowing. The config file and the publishable migrations are your sanctioned customization surface — add columns there rather than editing vendor files. If the package fires events on role assignment/revocation — and now on badge issue/revoke — subscribe to them for audit logging; that's cheaper than wrapping every call site and gives you a compliance trail for free.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect