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.
Package info
github.com/padosoft/laravel-iam-bridge-spatie-permission
pkg:composer/padosoft/laravel-iam-bridge-spatie-permission
Requires
- php: ^8.3
- padosoft/laravel-iam-client: ^1.0
- padosoft/laravel-iam-contracts: ^1.0
- padosoft/laravel-iam-server: ^1.0
- spatie/laravel-package-tools: ^1.16
- spatie/laravel-permission: ^6.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
This package is auto-updated.
Last update: 2026-07-05 16:23:33 UTC
README
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.
📖 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:
- Inventory — read your Spatie roles/permissions (read-only, touches nothing) and turn them into an IAM manifest.
- Shadow — Spatie keeps deciding for real; IAM decides in parallel on every
Gatecheck and records only the mismatches. Nobody is ever blocked or let in by IAM during this phase. - Enforce — once the mismatch log is clean (or every divergence is explained), flip
IAM_SPATIE_MODEtoenforce. 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 ofspatie/laravel-permissionstraight from its tables (SpatieScanner). Emitsinventory.json+ areport.mdthat flags the smells: empty roles, orphan permissions, direct user grants, multiple guards.iam:spatie:manifest— generate alaravel-iam.manifest.v2from the inventory (ManifestGenerator), ready foriam: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 startingriskheuristic for high-impact actions.ShadowGate— aGate::afteradapter that compares IAM vs Spatie on every ability and returnsnull, 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 cutover —
shadow⇄enforceis 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_APPprefix. - 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:
- Quickstart — the whole loop in one page.
- Core concepts and Shadow before cutover.
- Migration pipeline and ShadowGate internals.
- CLI reference.
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.
