›
byrcsc/laravel-dev-login · 1.x
Work out which gate returned the 404, and read the exception each misconfiguration throws.
Every failure in this package is either a gate refusing or an exception naming the config key to edit. This page maps the symptom to the cause.
Four separate checks produce the same 404, on purpose. A 403 would tell a stranger the page is there.
Ask the gatekeeper which one:
php artisan tinkerapp(ByRcsc\LaravelDevLogin\Gatekeeper::class)->passes();
app(ByRcsc\LaravelDevLogin\Gatekeeper::class)->hostIsAllowed('localhost');passes() is false when the enable flag, the environment allowlist, or the
production check disagrees. hostIsAllowed() covers the per-request gate.
Then check whether the routes exist at all:
php artisan route:list --name=dev-login| Result | Cause |
|---|---|
No routes, passes() is false | A boot-time gate: the flag, the environment, or production |
| Routes exist, page still 404s | The host gate, or you are on a different path |
| Routes exist at another URI | path was changed in config |
DEV_LOGIN_ENABLED is not set, is false, or is not reaching the process.
Check the value your application actually sees:
config('dev-login.enabled');A cached config file freezes the env() call. Rebuild it after changing the
variable:
php artisan config:cacheenvironments defaults to local and testing, matched case-sensitively.
Compare the two:
app()->environment();
config('dev-login.environments');A staging environment has to be written in explicitly. A value that is not a list of strings is read as an empty list, which shuts the gate.
allowed_hosts is checked against the host on the request, not against
APP_URL. Herd and Valet sites ending in .test are covered by the default.
Common misses:
.localhost or .ddev.site domain, which needs adding.*, which matches nothing. Wildcards only stand for the subdomain
part.*.test does not match test.The component behaves the same way. <x-dev-login::profiles /> renders an
empty string on a host nobody allowed, so an embedded page that shows no
buttons is this gate, not a profile problem.
Dev login is enabled while APP_ENV=production. Refusing to boot. Remove
DEV_LOGIN_ENABLED from the production environment, or set it to false.DevLoginEnabledInProduction, thrown while the package boots. Every request
fails, and so does artisan, because both boot the same container.
The fix is in the environment file, not in a cache command. Remove
DEV_LOGIN_ENABLED from the production environment or set it to false. The
refusal is loud because a silent no-op would be indistinguishable from a
package that is working.
Dev login profile [admin] resolves to admin@example.com, which does not exist.
Did you run your seeder?ProfileUserNotFound. The profile points at an address no user has. This is
the config-to-seeder drift detector, and it is the error you are most likely
to meet.
Check three things, in order:
config/dev-login.php matches the one in the seeder.guard points at the provider holding that user. The
shipped resolver looks the address up on the guard's own provider, so a
profile on the admin guard searches whatever admin names.The package never creates the missing user. If you want it created, write a resolver that does. See User resolvers.
Dev login profile [acme-owner] could not make the tenant [acme] current.TenantNotResolved. Your resolver threw, and whatever it threw is kept as the
previous exception, so the real cause is one level down the trace.
Nobody is authenticated when this happens. The tenant is made current before the user is resolved, so a tenancy failure can never leave you inside the wrong tenant.
Dev login profile [acme-owner] names the tenant [acme], but no
[tenant_resolver] is configured in config/dev-login.php.The package ships the TenantResolver contract and no adapter. Either
implement it and set tenant_resolver, or remove the tenant key from the
profile. See Tenancy.
There is no dev login profile [ghost]. Configured profiles: admin, member.ProfileNotFound. Usually a stale form or a hand-written URL after a profile
was renamed. The message lists the profiles that do exist.
InvalidProfile covers the profiles list, and every message names the
profile key.
| Message ends with | Fix |
|---|---|
is missing a [label], which every profile needs | Add label, or replace an empty string |
is missing a [email], which every profile needs | Add email |
has a [remember] that is not true or false | Use a real boolean, not 'yes' |
has a [tenant] that is not a string, an integer, or null | Tenants are scalars |
names the guard [ghost], which is not in config/auth.php | Fix the guard name |
names the guard [api], which is not a session guard | Session guards only; token and API guards are out of scope |
names the resolver [...], which does not implement ... | Implement UserResolver |
has settings this package does not know: guardd | A typo in a key name |
must be an array of settings | The profile value is a string, not an array |
An unknown key throws rather than being ignored, because a setting that quietly does nothing is worse than one that fails.
InvalidConfiguration covers the keys around the profile list, and each
message names the key to edit: path, middleware, resolver, and
tenant_resolver.
No profiles are configured. Add one to the profiles key in
config/dev-login.php.The profiles array is empty, or a published view replaced the component.
Check that the form posts. A GET to /dev-login/{profile} returns 405,
because there is no login-by-GET route.
The CSRF token expired, or web was removed from the middleware list. The
POST route relies on that group for both the session and CSRF protection.
The destination is the first of these that exists: the profile's redirect,
the session's intended URL, default_redirect, then /.
A profile that names a tenant skips the intended URL, because a URL captured
in one tenant rarely means anything in another. If a tenant profile ignores
somewhere you expected it to go, give it an explicit redirect.
A click ends in SessionGuard::login(), so Laravel's Login event fires and
your listeners run. Set fire_login_event to false on the profiles where
you want the account without the consequences.
The reverse case is a listener that does not run for a profile: check whether
that profile has fire_login_event set to false.
That is Laravel's session guard doing what a real login does, for a profile
with remember set to true. It is the only database write a dev login causes.