padosoft / laravel-iam-server
Server Laravel IAM: identity, organizations, Application Registry + manifest, PDP (RBAC+ABAC+ReBAC), OAuth (league/oauth2-server) + OIDC layer, audit tamper-evident, governance/IGA, Admin API + panel.
Requires
- php: ^8.3
- lcobucci/jwt: ^5.0
- league/oauth2-server: ^9.0
- padosoft/laravel-iam-contracts: ^1.0
- spatie/laravel-package-tools: ^1.16
- symfony/psr-http-message-bridge: ^7.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
Suggests
- aws/aws-sdk-php: Driver AWS KMS/Secrets Manager (M3)
- directorytree/ldaprecord-laravel: LDAP/AD — padosoft/laravel-iam-directory
- laravel/ai: Modulo AI — padosoft/laravel-iam-ai
- laravel/fortify: Auth backend nativo (M5)
- laravel/mcp: MCP server — padosoft/laravel-iam-mcp
- laravel/passkeys: WebAuthn/passkey, assurance AAL2 (M5)
- laravel/socialite: Federated login (M5)
- padosoft/laravel-iam-rebel: Assurance avanzata (step-up/SCA/bot)
This package is auto-updated.
Last update: 2026-06-29 13:07:18 UTC
README
Laravel IAM — Server
A self-hostable Identity & Authorization control plane for Laravel.
An OAuth2 / OIDC identity provider, a RBAC + ABAC + ReBAC policy decision point, tamper-evident audit,
IGA governance and an admin panel — in one composer package you own.
Why this package
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 idea who can do what or who decided
it. Renting an IdP (Auth0, Okta, Entra) fixes login but leaves authorization — and your audit trail — off
in someone else's cloud, metered per MAU.
laravel-iam-server is the control plane you host yourself. It is at once:
- an identity provider — OAuth2 (
league/oauth2-server) + an OIDC layer, with sessions you can revoke; - 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, SoD;
- 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.
Features
- Deterministic PDP —
NativeSqlEngineevaluates RBAC + ABAC (attribute conditions) + ReBAC (relationship lookups:listSubjects/listResources) in one pass, deny-overrides, fail-closed. EveryDecisioncarries adecision_id, the matched policies and a human-readableexplanationyou can cite in audit. - Application Registry + manifests — apps submit a manifest of their permissions/roles/scopes/conditions; it is validated, diffed, approved, applied and rollback-able. The core hardcodes nothing.
- Full OAuth2 + OIDC IdP — 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).
- Tamper-evident audit — hash-chained events (
AuditChainAppender/AuditChainVerifier), SIEM export, webhooks/outbox, and GDPR crypto-shredding / legal-hold for PII. - Identity governance (IGA) — access-review campaigns, access-request approval flows, least-privilege recommendations, separation-of-duties, anomaly signals — each gated per layer/app/role/user via a feature scope.
- Assurance / step-up — NIST 800-63B assurance levels; the PDP can require step-up (AAL2) for critical actions.
- Admin API + panel — every admin route is documented in
resources/openapi.yaml(enforced by a test), protected by theiam.canpermission middleware, with idempotency keys on writes. - Observability — health/readiness endpoints and a pluggable tracer (
NullTracer/LogTracer).
Use cases
- Be your organization's IdP. Run OAuth2/OIDC login for all your apps, on infrastructure you control.
- Centralize authorization. Many apps, one PDP: each asks
check()instead of re-implementing roles. - Pass an audit. Hash-chained, verifiable history + access reviews and SoD give you the evidence compliance asks for.
- Escape scattered gates. Migrate apps off ad-hoc
Gate/spatiepermissions onto declared manifests (see the migration bridge).
Web Admin Panel
A React + Vite + Tailwind console, driven only through the Admin API.
Dashboard — posture at a glance.
| Applications & manifests | Audit trail |
|---|---|
![]() |
![]() |
| Roles & permissions | Access reviews |
|---|---|
![]() |
![]() |
| Policy playground | Anomalies |
|---|---|
![]() |
![]() |
The full set of screens (users, sessions & tokens, organizations, events & webhooks, settings…) lives in
art/screenshots/.
Installation
composer require padosoft/laravel-iam-server
Requirements: PHP 8.3+, Laravel 13. A database (MySQL/PostgreSQL/SQLite).
Publish config and run migrations:
php artisan vendor:publish --tag="laravel-iam-server-config"
php artisan migrate
The service provider auto-registers the Admin API, OAuth and OIDC routes, and the iam.can /
iam.admin_auth / iam.idempotency middleware.
Quick start
1. Register an application and its manifest
Each consuming app declares what it needs. A manifest lists permissions/roles (slugs are immutable
app_key:permission):
{
"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 through the Admin API (POST /manifests), then approve and apply it — the registry
validates and diffs before anything changes.
2. Ask the PDP
The decision point is the only authority on allow/deny. Build a DecisionQuery and call the engine:
use Padosoft\Iam\Domain\Authorization\Pdp\NativeSqlEngine; use Padosoft\Iam\Domain\Authorization\Pdp\DecisionQuery; use Padosoft\Iam\Contracts\Support\SubjectRef; $decision = app(NativeSqlEngine::class)->decide(new DecisionQuery( subject: new SubjectRef('user', '42'), permission: 'warehouse:stock.adjust', organizationId: 'org_123', context: ['amount' => 500], // evaluated by the ABAC condition above explain: true, )); $decision->allowed; // bool — deny-overrides, fail-closed $decision->decisionId; // citable in your audit log $decision->explanation; // why it decided this way $decision->requiresStepUp; // true ⇒ ask the user for AAL2 first
3. Or over HTTP
curl -X POST https://iam.example.com/admin/decisions/check \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"subject":"user:42","permission":"warehouse:stock.adjust","context":{"amount":500}}'
In your consuming apps you normally don't call the PDP directly — you install
padosoft/laravel-iam-clientand protect routes with itsiam.canmiddleware / Gate adapter, which caches decisions and verifies JWTs.
Ecosystem
| Package | Role |
|---|---|
| laravel-iam-contracts | Shared interfaces & DTOs — 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 apps consuming IAM: OIDC login, JWT/JWKS, iam.can middleware, Gate adapter |
| laravel-iam-ai | Optional AI module: advisory-only governance (redaction + hallucination guard + audit) |
| laravel-iam-directory | Optional directory module: LDAP / Active Directory; SCIM in v2 |
| laravel-iam-bridge-spatie-permission | Migration bridge from spatie/laravel-permission: scan, shadow mode, decision diffing, cutover |
Documentation
A docmd doc-site lives in docs/: start at docs/index.md, then
Getting started, Concepts, and the subsystem guides —
Policy Decision Point, OAuth2 & OIDC, Audit,
Governance, Admin API and Admin panel.
The full HTTP contract is in resources/openapi.yaml.
Security
Laravel IAM is fail-closed by design: default-deny, deny-overrides, and any error resolves to deny.
Every mutation is hash-chained and verifiable (audit/verify-chain); cross-tenant access returns 404,
not 403; secrets use envelope encryption and PII is crypto-shreddable. OAuth is league/oauth2-server and
the OIDC layer is MIT (never AGPL). Please report security issues to security@padosoft.com rather than
opening a public issue.







