padosoft/laravel-iam-bridge-spatie-permission

Bridge di migrazione da spatie/laravel-permission a Laravel IAM: scan, manifest generation, shadow mode, decision diffing, cutover, rollback.

Maintainers

Package info

github.com/padosoft/laravel-iam-bridge-spatie-permission

pkg:composer/padosoft/laravel-iam-bridge-spatie-permission

Transparency log

Statistics

Installs: 168

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.2.0 2026-07-05 16:23 UTC

This package is auto-updated.

Last update: 2026-07-05 16:23:33 UTC


README

Laravel IAM

Laravel IAM — Spatie Permission Bridge

Migrate from spatie/laravel-permission to Laravel IAM without a big-bang cutover.
Scan your existing permissions, generate the IAM manifest, run both systems in shadow, diff every decision, and switch over only when the diff is clean — with a rollback always one env var away.

Tests Latest Version on Packagist Total Downloads PHP Version License

📖 Full documentation: doc.laravel-iam-bridge-spatie-permission.padosoft.com

Why this package

spatie/laravel-permission is a great in-app RBAC library — until you outgrow it. Multiple apps, a real audit trail, ABAC/ReBAC conditions, governance and access reviews, an OAuth/OIDC provider: that's a control plane, and that's Laravel IAM. But you can't just flip authorization for a live application and hope. One wrong mapping and people are locked out of production — or worse, let in.

This bridge makes the move boring and reversible. It treats migration as three observable phases:

  1. Inventory — read your Spatie roles/permissions (read-only, touches nothing) and turn them into an IAM manifest.
  2. Shadow — Spatie keeps deciding for real; IAM decides in parallel on every Gate check and records only the mismatches. Nobody is ever blocked or let in by IAM during this phase.
  3. Enforce — once the mismatch log is clean (or every divergence is explained), flip IAM_SPATIE_MODE to enforce. IAM becomes the authority; Spatie stays as a read-only cache. Roll back by flipping the env var back.

You cut over on evidence, not hope.

Features

  • iam:spatie:scan — read-only inventory of spatie/laravel-permission straight from its tables (SpatieScanner). Emits inventory.json + a report.md that flags the smells: empty roles, orphan permissions, direct user grants, multiple guards.
  • iam:spatie:manifest — generate a laravel-iam.manifest.v2 from the inventory (ManifestGenerator), ready for iam:manifest:validate / iam:app:register.
  • iam:spatie:sync — regenerate the manifest from the CURRENT spatie state and sync it to IAM in one shot (diff → apply additive changes, or submit gated ones for approval). Schedule it for hands-off drift-sync; removed roles/permissions are deprecated (kept for history, disabled), not deleted.
  • PermissionMapper — deterministic, idempotent slugging of Spatie names ("orders.refund", "Manage Users") to IAM keys (^[a-z][a-z0-9_.-]*$); two names that collapse to the same key surface a semantic duplicate to review. Includes a starting risk heuristic for high-impact actions.
  • ShadowGate — a Gate::after adapter that compares IAM vs Spatie on every ability and returns null, so it never changes the live outcome. It probes Spatie directly (hasPermissionTo) instead of trusting a possibly short-circuited gate result — so you never cut over on false-zero mismatches.
  • RecordsMismatch / MismatchRecorder — a pluggable sink for divergences (structured log by default; swap it to push to a dashboard or review queue).
  • Reversible cutovershadowenforce is a single env var. Read-only write-protection keeps Spatie consistent after cutover.

Use cases

  • Prove decision parity to stakeholders before switching: run shadow in production traffic for a week, show a clean mismatch log, then cut over.
  • Migrate app-by-app in a fleet: each application gets its own manifest and IAM_SPATIE_APP prefix.
  • De-risk a compliance-driven move: keep the exact same enforcement behavior while you build confidence in the new control plane, with an instant rollback path.

Installation

composer require padosoft/laravel-iam-bridge-spatie-permission
php artisan vendor:publish --tag=iam-spatie-config

Requirements: PHP 8.3+, Laravel 13+, an existing spatie/laravel-permission install, and a reachable Laravel IAM server (via padosoft/laravel-iam-client).

Quick start

The bridge ships in shadow mode by default (IAM_SPATIE_MODE=shadow) — installing it changes no authorization behavior. Follow the runbook:

1. Inventory your current setup

php artisan iam:spatie:scan --output=storage/app/iam/spatie-inventory
# → inventory.json + report.md (roles, permissions, empty roles, direct grants, guards)

Open report.md and clean up the smells (inconsistent naming, semantic duplicates, critical permissions).

2. Generate the IAM manifest

php artisan iam:spatie:manifest --app=billing --name="Billing" \
  --output=storage/app/iam/iam.manifest.json

The manifest is a proposal: review the inferred risk levels and roles, then validate it on the server:

php artisan iam:manifest:validate storage/app/iam/iam.manifest.json
php artisan iam:app:register      storage/app/iam/iam.manifest.json

Keeping IAM in sync as your roles/permissions change

After the first registration, your spatie roles/permissions keep evolving. You don't hand-edit the manifest every time — iam:spatie:sync regenerates it from the current spatie state and pushes it to IAM in one step:

# see what would change (no writes)
php artisan iam:spatie:sync --app=billing --dry-run

# additive changes (new roles/permissions) apply straight away; a removal is gated → --approve applies it
php artisan iam:spatie:sync --app=billing --approve

Schedule it for hands-off drift-sync (host routes/console.php):

Schedule::command('iam:spatie:sync --app=billing --approve')->hourly();

What happens to a removed role or permission? IAM never hard-deletes it. It is deprecated — the row stays (full history + audit intact) but is disabled: it can no longer be granted or satisfy a decision. Re-adding the same key in a later sync re-activates it. This is safe by design: you keep the record and can prove who could do what, when. (See Keeping IAM in sync.)

3. Observe in shadow

With IAM_SPATIE_MODE=shadow, the ShadowGate is registered automatically. Every Gate check is mirrored to IAM and divergences are logged as iam.shadow.mismatch:

// Your existing code — unchanged. Spatie still decides.
Gate::authorize('orders.refund', $order);
// In the background: IAM evaluates billing:orders.refund and logs only if it disagrees with Spatie.

Point the mismatch channel wherever your reviewers look:

IAM_SPATIE_APP=billing
IAM_SPATIE_MISMATCH_CHANNEL=iam-shadow

4. Review mismatches & cut over

When the mismatch log is clean (or every entry is explained), flip the mode:

IAM_SPATIE_MODE=enforce

IAM is now the authority (enforcement comes from the client's Gate adapter); Spatie becomes a read-only cache. Rollback is the same switch in reverse: set IAM_SPATIE_MODE=shadow and you are back to Spatie deciding, instantly.

Ecosystem

Package Role
laravel-iam-contracts Shared interfaces & DTOs — the dependency root
laravel-iam-server The IAM server: identity, PDP (RBAC+ABAC+ReBAC), OAuth/OIDC, audit, governance, Admin API & panel
laravel-iam-client Client for apps consuming Laravel IAM: OIDC login, JWT/JWKS, middleware, Gate adapter
laravel-iam-bridge-spatie-permission (this repo) Zero-downtime migration off spatie/laravel-permission
laravel-iam-ai Optional AI module: advisory-only governance (redaction + hallucination guard + audit)
laravel-iam-directory Optional directory module: LDAP / Active Directory (LdapRecord); SCIM in v2

Documentation

The full documentation site (25+ pages: guides, concepts & theory, architecture, ADRs, operations, and a complete CLI/PHP/manifest reference) is published at doc.laravel-iam-bridge-spatie-permission.padosoft.com and its source lives in docs-site/.

Highlights:

Security

Migration is fail-closed and non-destructive: the scanner is read-only, shadow mode never alters a live decision, and decision diffing uses deny-overrides — when in doubt, deny. Permission and role slugs are immutable (app_key:permission). If you discover a security issue, email security@padosoft.com rather than opening a public issue.

License

MIT © Padosoft. See LICENSE.