laravel-iam-server
padosoft/laravel-iam-servergives every app in your estate one place to log in, one place to decide who can do what, and one place to prove it later — running inside infrastructure you own, as a composer package, not a metered SaaS.
An OAuth2 / OIDC identity provider, a deterministic RBAC + ABAC + ReBAC policy decision point, a tamper-evident hash-chained audit log, an IGA governance suite, and a React admin panel — all driven through a single Admin API.
In a few minutes you’ll know exactly what this package is, the problem it solves, why it beats renting an
IdP or hand-rolling permissions, and where to click next. Every other page goes deeper — this one gives
you the whole picture.
What it is — in one minute
Most teams end up with authorization scattered across every app: a spatie/permission table here, a pile
of Gate::define() closures there, hand-rolled OAuth somewhere else, and no single answer to “who can do
this, and who granted them that?”. Renting an IdP (Auth0, Okta, Entra) fixes login but leaves
authorization — and your audit trail — in someone else’s cloud, metered per monthly active user.
laravel-iam-server is the control plane you host yourself. It is at once:
- an identity provider — OAuth2 on
league/oauth2-serverplus an
OIDC layer, with server-side, revocable sessions; - a policy decision point (PDP) — one deterministic engine that answers “can subject X do permission Y
on resource Z?” with RBAC + ABAC + ReBAC, deny-overrides, fail-closed, and a citable
explanation; - a tamper-evident audit log — every mutation hash-chained and verifiable, exportable to your SIEM;
- an identity governance (IGA) suite — access reviews, access requests/approvals, least-privilege
recommendations and separation-of-duties; - an admin panel — a React console driven entirely through the Admin API (no UI ever touches the DB).
Apps stop owning authorization logic. They declare their permissions/roles/scopes in a manifest, and
ask the PDP. You get one place to see and prove every access decision.
In one line: the shortest path from “every app reinvents auth” to “one IdP, one decision point, one
audit log — self-hosted and provable”.
The problem it solves
| Without laravel-iam-server | With laravel-iam-server |
|---|---|
| Every app grows its own roles table and its own copy of “who is an admin”. | Apps declare permissions in a manifest; one PDP decides for all of them. |
| Login is centralized (an external IdP) but decisions are not — nobody can answer “who can adjust stock?”. | One check() entrypoint, deterministic and explainable, is the only allow/deny authority. |
| Authorization logic and your audit trail live in a vendor’s cloud, metered per MAU. | You own the control plane: a composer package on your own database. |
| “Who approved this access?” means reading code in five repositories. | A hash-chained, verifiable audit log records every grant, decision and approval. |
| An error in an auth check quietly allows an action. | Fail-closed: any error, malformed query or missing data resolves to deny. |
| Passing an access audit means screenshots and spreadsheets. | Access-review campaigns, SoD and least-privilege produce diffable, signed evidence. |
Who it’s for
You run many Laravel apps and want one IdP and one authorization service on infrastructure you control,
instead of a per-MAU SaaS bill.
You need verifiable audit, access reviews, separation-of-duties and least-privilege evidence — the
artifacts an auditor actually asks for.
You’re tired of re-implementing roles in every service. Declare a manifest, install
laravel-iam-client, and protect routes with
iam.can.
You must prove tamper-evidence, PII crypto-shredding under GDPR, and step-up assurance on sensitive
actions — as auditable, exportable records.
Why it’s different — the moats
NativeSqlEngine evaluates RBAC + ABAC + ReBAC in one pass, deny-overrides, fail-closed. Every
Decision carries a decisionId, the matched policies and a human-readable explanation you can cite
in audit.
Apps submit a manifest of their permissions/roles/scopes/conditions; it is validated, diffed,
approved, applied and rollback-able. The core hardcodes nothing — this is the real moat.
Authorization-code/PKCE, client-credentials, refresh (encrypted), JWKS, an OIDC layer on an MIT
base (never AGPL). Bring your own login backend — Fortify, Socialite, passkeys.
Hash-chained events (AuditChainAppender / AuditChainVerifier), SIEM export, a transactional outbox
for webhooks, and GDPR crypto-shredding / legal-hold for PII.
Access-review campaigns, multi-step access-request approvals, least-privilege recommendations and SoD —
each gated per layer / app / role / user via a feature scope.
Every admin route is documented in resources/openapi.yaml (enforced by a test), protected by the
iam.can middleware, idempotent on writes. The panel is just another client.
How it fits together
A consuming app declares a manifest and asks the PDP; the PDP evaluates declared policy and emits a decision
that is logged into the hash-chained audit. Governance and the admin panel observe and steer the same data.
Start in 60 seconds
Install the package
composer require padosoft/laravel-iam-server php artisan vendor:publish --tag="laravel-iam-server-config" php artisan migrateThe service provider auto-registers the Admin API (
/api/iam/v1), OAuth and OIDC routes, and the
iam.can/iam.admin_auth/iam.idempotencymiddleware.Register an application and its manifest
{ "app_key": "warehouse", "permissions": [ { "key": "warehouse:stock.read", "label": "Read stock" }, { "key": "warehouse:stock.adjust", "label": "Adjust stock", "condition": { "attr": "amount", "op": "<=", "value": 1000 } } ], "roles": [ { "key": "warehouse:operator", "permissions": ["warehouse:stock.read", "warehouse:stock.adjust"] } ] }Submit it (
POST /applications/{app}/manifests), then approve and apply — validated and diffed
first.Ask the PDP
$decision = app(\Padosoft\Iam\Domain\Authorization\Pdp\NativeSqlEngine::class)->decide( new \Padosoft\Iam\Domain\Authorization\Pdp\DecisionQuery( subject: new \Padosoft\Iam\Contracts\Support\SubjectRef('user', '42'), permission: 'warehouse:stock.adjust', organizationId: 'org_123', context: ['amount' => 500], explain: true, ) ); $decision->allowed; // bool — deny-overrides, fail-closed $decision->explanation; // citable in your audit log
→ Quickstart · → Installation · → Core concepts
Ecosystem
laravel-iam-server is the server of the Laravel IAM family. The consumable packages:
| Package | Role |
|---|---|
| laravel-iam-contracts | Shared interfaces & DTOs (PDP, KeyProvider, Assurance, FeatureScope) — the dependency root |
| laravel-iam-server (this repo) | The control plane: identity, PDP, OAuth/OIDC, audit, governance, Admin API & panel |
| laravel-iam-client | Client for consuming apps: OIDC login, JWT/JWKS, iam.can middleware, Gate adapter, policy cache, webhook receiver |
| laravel-iam-ai | Optional AI module: advisory-only governance (redaction + hallucination guard + audit) |
| laravel-iam-directory | Optional directory module: LDAP / Active Directory (LdapRecord) |
| laravel-iam-bridge-spatie-permission | Migration bridge from spatie/laravel-permission: scan, shadow mode, cutover, rollback |
| laravel-iam-node | Node/TS client SDK — thin + fail-closed |
| laravel-iam-react-native | React Native client SDK — thin + hooks |
| laravel-iam-rust | Rust client SDK — async + blocking, fail-closed |