gdbots/iam
gdbots/iam is a PHP identity and access management package for Gdbots applications, providing users, roles, permissions, and authentication/authorization helpers to manage accounts and enforce access control across services.
Require the package (and expect to pull transitive gdbots deps):
composer require gdbots/iam
You will almost always drag in gdbots/pbj (message/schema runtime) and often gdbots/ncr (schema registry) and gdbots/pbjx (message bus) alongside it. Check composer.json and the lockfile before assuming gdbots/iam alone is enough. As of v3.0.0 you should expect the transitive gdbots packages to have made matching PHP 8.1 releases — resolve them together, not one at a time.
Check compatibility before you build a plan around it. v3.0.0 is a major, breaking release whose only stated change is dropping older PHP support in favour of PHP 8.1. Treat that as: the package now floors at PHP 8.1, which lines it up with Laravel 10 (PHP 8.1) and Laravel 11/12 (PHP 8.2) rather than ruling them out. Read the actual require.php constraint in composer.json — ^8.1 usually means 8.2/8.3 are fine, but a pinned 8.1.* would not be. Verify it installs cleanly before committing to it.
If you are upgrading from 2.x, do it on a branch and diff src/. The release notes claim the only change is PHP support, but this package's src/ is generated — regeneration can silently alter defaults, field presence, or validation even when the changelog says nothing. git diff the message classes between tags before you trust "no schema changes".
Look at src/ first, then the schemas. This package is essentially generated + hand-written implementations of gdbots:iam protobuf schemas. The classes you will actually touch are the message objects (User, Role, Permission, App, and related command/event messages, depending on the schema version). Each one exposes the standard Pbj interface: fromArray(), toArray(), schema(), field accessors, and validation.
Understand what it is not. It is not an auth guard, not a user provider, not a persistence layer. It is a data/schema library. You still need a repository, an event store or DB, and all the HTTP/auth glue yourself. The v3.0.0 bump does not add any Laravel integration.
First useful use case: parse an IAM payload coming from a gdbots-based backend (or serialize one to send to it):
use Gdbots\Iam\User;
$user = User::fromArray(json_decode($payload, true));
echo $user->getEmail();
$json = json_encode($user->toArray());
If that round-trip works in a tinker session, you have validated the main integration surface.
Treat IAM messages as DTOs at the boundary. Map incoming gdbots messages to your Eloquent models (or vice versa) in a dedicated mapper class rather than letting Gdbots\Iam\* objects leak through controllers, policies, or Blade views. This keeps your Laravel code idiomatic and lets you swap the backing store later.
Wrap message construction in a factory/service. Message classes use a static construction pattern plus schema-registry validation. A thin GdbotsIamFactory bound in a service provider keeps new User() / User::fromArray() calls in one place and makes testing easier:
// AppServiceProvider::register()
$this->app->singleton(IamMessageFactory::class, function () {
return new IamMessageFactory(); // wraps Ncr/Pbj if you configure it
});
Use fromArray/toArray as your persistence boundary. For most Laravel apps the practical pattern is: store the toArray() JSON in a json column (or event store table), rehydrate with fromArray() on read. This preserves schema fidelity without needing the full gdbots infrastructure.
Pin the major version in composer.json. With a 3.x line now in play, "gdbots/iam": "^3.0" protects you from the next surprise major. If you are still on 2.x and cannot move PHP yet, pin ^2.0 explicitly rather than floating.
If you need commands/events (e.g. GrantRole, RevokeRole), wire up gdbots/pbjx. The IAM schemas define command/event messages intended to run over a message bus. Bind the Pbjx dispatcher in a service provider and dispatch from Laravel jobs/listeners. Do not roll your own dispatch — the schemas assume the Pbjx lifecycle (command → transaction → events). Note that pbjx itself will need a PHP 8.1-compatible release to pair with iam v3.
Use curies for cross-system references. IAM schemas reference other nodes by Curie strings (e.g. pbj:gdbots:iam:user:1-0-0). When you store references in your DB, store the Curie, not the raw ID, so you can resolve across services consistently.
Validate at the edge, then trust internally. Call the message's validation (validate() / Pbj's validation hooks) right after fromArray() on user input. Once validated, you can pass the message object around without re-checking.
Integrate with Laravel auth cautiously. If you must back Laravel's Authenticatable with a gdbots User, implement a custom UserProvider that hydrates your Eloquent model and uses the IAM User message only for the sync step. Do not attempt to implement Authenticatable directly on a generated Pbj class.
For read-heavy endpoints, cache the decoded array, not the message. Pbj message objects are not lightweight and are not designed to be serialized into Laravel's cache. Cache toArray() output.
It is generated code — do not hand-edit. src/ is produced from the upstream .proto schemas. Any manual change is lost on regeneration and will diverge from the schema registry. If you need extra behavior, wrap the message in your own class. This matters more, not less, now that a new major has landed: your local patches are the first thing the upgrade overwrites.
PHP 8.1 is now a hard floor. The whole 3.x line is the "PHP 8.1+" line. If you are running Laravel 9 on PHP 8.0 you cannot take this release at all; if you are on 8.1/8.2/8.3 you are in scope. Check require.php for an exact pin before assuming 8.3 works.
No Laravel integration ships with it. There is no service provider, facade, config file, or migration. Everything (binding Ncr, configuring Pbjx, storage) is on you. A Symfony bundle (gdbots/iam-bundle) exists in the ecosystem, which tells you the intended audience is Symfony/framework-bundled apps, not Laravel. v3.0.0 did not change this.
Dependency gravity is high. gdbots/iam pulls gdbots/pbj; real usage typically adds gdbots/ncr and gdbots/pbjx. The PHP 8.1 requirement means you must upgrade that entire constellation in lockstep — a mixed set (iam 3.x against a pbj that still requires 7.4) is the most likely failure mode of this upgrade. Run composer why gdbots/pbj whenever something breaks.
Strict field handling. Pbj messages reject unknown fields on fromArray() and are strict about field types. If your payload has extra keys or stringified numbers where ints are expected, you get exceptions, not silent coercion. Sanitize before hydrating.
Curie format is load-bearing. Schema identifiers look like pbj:vendor:package:message:x-y-z. A single typo in a Curie fails at registry lookup time with a not-found error that doesn't clearly point at the offending string. Grep for the Curie and compare character by character. If v3 regenerated schemas, double-check any Curie you hardcoded.
Debugging tip: when a message fails to hydrate, dump $payload and the message's schema() and compare field names exactly. Pbj field names are snake_case in arrays and camelCase in accessors — mixing them is a common source of "missing field" errors.
No migrations, no storage, no indexes. This package gives you the object model only. Whatever persistence you choose (event store, JSON column, Redis) is yours to design, and the schemas will not tell you how to query efficiently. Design your indexes around what you need before storing blobs.
Extension points: custom schemas are registered via Ncr (the node component registry). If your app defines additional IAM-adjacent messages, register them alongside the built-in ones in a bootstrap step so Pbj can resolve them — otherwise Curie resolution fails at runtime, not at build time.
Maturity signal (updated): a v3.0.0 is a pulse — the package is not abandoned, and the PHP 8.1 floor means it is at least installable on contemporary Laravel. But a release whose entire changelog is "support PHP 8.1", after a dormant stretch and with no Laravel integration added, is maintenance, not momentum. Still treat it as a low-adoption dependency: wrap it behind an interface, keep the mapper layer, pin the major, and be prepared to vendor or fork. For most teams the safer move remains to consume the IAM schemas (as JSON/proto) and generate your own DTOs rather than depending on this runtime.
How can I help you explore Laravel packages today?