orchestra/auth
Orchestra Auth is an authentication extension for the Orchestra Platform, built on Laravel's native auth system. It provides user login, registration, password management, and access control scaffolding to secure your Orchestra-powered applications.
Confirm the platform before you write a line of code. orchestra/auth is the auth component of Orchestra Platform, not a general-purpose Laravel auth package. Your first day-to-day question is binary: is this app Orchestra-based? If yes, the package is already the sanctioned layer and you mostly need to learn its conventions. If no, stop — Laravel's native auth plus a mainstream RBAC package is the shorter path, and adopting this one is a platform migration in disguise.
Install and pin to the patched release.
composer require "orchestra/auth:^5.0.1"
The only release evidence you have is v5.0.1, which swapped the config() helper for the Config facade to fix Class config not found during migrate:rollback from a testing environment. Pin at or above it — and if anyone has previously copied the package's migrations into database/migrations, diff those local copies, because the pin won't fix forked files.
Prove migrations are reversible on day one. This is the exact regression class the patch addressed, so make it your first act, not a sprint-end chore:
php artisan vendor:publish # inspect the provider list first; names vary by version
php artisan migrate
php artisan migrate:rollback # this must succeed, not just migrate
php artisan migrate
If vendor:publish lists nothing, copy the config stub manually and diff it against config/auth.php so you know exactly which keys the package expects.
Read these four things, in this order, before your first feature:
config/auth.php — guards, providers, and the model the eloquent provider points at. Everything else hinges on this seam.User, Role, permission/ACL model) — read $table, $fillable, $hidden, and the relationship methods before writing any query.The source in vendor/orchestra/auth is your documentation. There is no community corpus for this package.
Your first use case: one role, one protected route, one test. Seed a role, attach it to a user, protect a route, and assert both the allow and deny cases. That single pass exercises config → model → guard → authorization boundary and surfaces table-name and model-extension mismatches while they're still cheap to fix.
Daily dev loop to establish immediately:
php artisan tinker
>>> $u = App\Models\User::first();
>>> $u->roles; # confirm relationship shape early
>>> Gate::forUser($u)->allows('manage-billing'); # split ability vs. data failures
That Gate::forUser(...)->allows(...) call is the single most useful debugging primitive you'll have with this package — it tells you in one step whether a denial comes from the ability definition or from missing role data.
Keep Laravel's auth surface at every call site. The package extends Laravel guards and providers rather than replacing them. Controllers, middleware, and Blade should stay auth()->user(), @auth, ->middleware('auth'), @can. Treat every deviation as a signal: either it's Orchestra behavior you must know about, or it's an abstraction you don't need. This keeps daily code familiar and limits lock-in even though the platform dependency is sticky.
Gate and Policy are the boundary; roles/ACL are the data source. The recurring failure with any RBAC package is authorization logic leaking into controllers and views. Define abilities once, check them everywhere:
// app/Providers/AuthServiceProvider.php
Gate::define('manage-billing', fn ($user) => $user->hasRole('billing-admin'));
Then @can('manage-billing') in views and $this->authorize('manage-billing') in controllers. Only the Gate::define bodies touch the package's role API. If you ever migrate off Orchestra, you rewrite the provider and nothing else — this is the highest-leverage pattern in the whole assessment.
Seed roles and permissions; never migrate them. Permission data belongs in seeders or an idempotent firstOrCreate bootstrap. Migrations that insert rows cause duplicate-key failures on re-run and rollback bugs in CI. Your daily migrate:fresh --seed loop stays reliable this way.
Extend the user model deliberately, then update the provider. If you need extra fields or behavior:
// app/Models/User.php extends the shipped model
// config/auth.php
'providers' => [
'users' => ['driver' => 'eloquent', 'model' => App\Models\User::class],
],
Half-migrations here — subclassing but leaving a stale providers.users.model — produce "wrong user instance" bugs that are painful to trace. Change both in the same commit and let tests confirm.
Keep table names in config, never in vendored code. Overriding table names is supported; editing published migrations in place is not. Any rename touches the config value and the migration together, plus a fresh-install test.
Split guards when you have an admin surface. Distinct web and admin guards with their own providers keep privilege boundaries legible and survive an admin-panel refactor. A single guard plus a role check is not equivalent once admin has its own session semantics.
Test with RefreshDatabase plus a role seeder, and pin a rollback job in CI. A cheap CI job that runs migrate then migrate:rollback against a throwaway database converts the only documented regression class here from a staging incident into a red build.
PR review checklist for auth-touching changes:
auth()/Gate?config/auth.php without a test asserting the resolved guard/provider?The v5.0.1 fix is a lesson, not just a patch. It replaced config() with the Config facade to avoid Class config not found during migrate:rollback in test environments. Two actions follow: pin ^5.0.1, and audit any locally forked migrations — your copies still carry the bug and the pin won't save you.
Config caching silently breaks auth config changes. With config:cache active, .env changes to guards, providers, or table names are ignored, and the symptom is "works locally, can't log in on staging." Always re-cache after touching auth config, and validate on an image that boots with caching enabled — not just under php artisan serve.
Rollback ordering with foreign keys. Users, roles, and pivot/ACL tables have FK dependencies. Custom migrations that touch these tables can break migrate:rollback ordering. Test rollback after each schema addition, not at the end of the sprint.
No community safety net. Zero dependents means no Stack Overflow corpus, no third-party bug reports, no upgrade posts. Debugging starts with reading vendor/orchestra/auth and the Orchestra core it depends on. Budget for that in estimates — a two-hour problem in a popular package can be a two-day problem here, so assign a named owner for the auth dependency.
Verify security-sensitive paths yourself before production. The docs say this is built on Laravel's native auth but do not document deviations in password reset, email verification, remember-me, or session invalidation. Write explicit tests for each against staging data. For an auth dependency at this adoption level, "probably delegates to Laravel" is not an acceptable posture.
Expect the Orchestra upgrade treadmill to drive this package. Orchestra majors force auth majors, and an auth upgrade sits directly on the login path. Read Orchestra release notes, not just this package's, and stage every upgrade behind a login/reset/authorization smoke test — never a routine composer update.
Extension points, in order of preference:
Gate::define calls.config/auth.php.Avoid editing published migrations or vendor source — both turn the next patch release into a manual merge.
Debugging recipe for "permission not working":
$u = User::find($id);
Gate::forUser($u)->allows('ability'); // false + roles present => ability definition bug
// false + roles missing => data/relationship bug
That split resolves most reports in one step instead of three. For deeper issues, tinker against a staging user to inspect role/ACL relationships directly, and check the resolved guard/provider with auth()->guard('web')->getProvider() to confirm you're hitting the model you think you are.
How can I help you explore Laravel packages today?