laravel/framework: Fix undefined array key in GenericUser::getAuthPassword() for passwordless users (#61876)
Contributed by tommie1001
Part of the framework v13.35.0 release
Category: Bug fix
Pull request #61876 fixes a small but noisy regression in the authentication layer: logging in a GenericUser that has no password attribute triggered an Undefined array key "password" warning on every login. The warning dates back to #61594, which taught SessionGuard::login() to store the user's password hash in the session. Logins still succeeded, but each one emitted a PHP warning that flowed straight into error trackers. If you're upgrading from v13.34.0, this patch makes GenericUser::getAuthPassword() return null instead of reading a missing array key, so passwordless logins are silent again — which is what the guard expected all along.
What changed
The patch is a one-line change in Illuminate\Auth\GenericUser:
// Before (v13.34.0)
public function getAuthPassword()
{
return $this->attributes[$this->getAuthPasswordName()];
}
// After
public function getAuthPassword()
{
return $this->attributes[$this->getAuthPasswordName()] ?? null;
}
The docblock return type widens from @return string to @return string|null. A new test, testLoginWithGenericUserWithoutPasswordDoesNotStorePasswordHash, covers the fix: it logs in a GenericUser created with only an id, installs a temporary error handler to capture warnings, and asserts that no warning fired, that the session still contains the user's ID, and that no password_hash_default key was written.
Why passwordless logins started warning
Since #61594, SessionGuard::login() calls getAuthPassword() on every login so it can store the password hash alongside the session ID. That is a security feature: the stored hash lets the guard invalidate the session when the user's credential changes.
GenericUser is the non-persistent user object that custom user providers commonly hand back — a thin wrapper around an arbitrary attributes array. For accounts that authenticate through an external identity provider (OAuth, SSO, API tokens), there is often no local password at all, so the password key simply is not present. The guard's unconditional call to getAuthPassword() performed a direct array read on that missing key, and PHP raised the warning before the guard could decide what to do with the value.
The result was easy to miss locally but painful in production: every request that logged a user in — for example, after session expiry — pushed one more Undefined array key "password" warning into Sentry, Bugsnag, Flare, or your log files.
The guard was already prepared for a missing password
The session guard's newer code paths already treat "no password" as a normal state:
login()only writespassword_hash_{guard}whengetAuthPassword()returns a truthy value;- #61532 handles a
nullpassword inuserFromRecaller(); - #61817 lets
hashPasswordForCookie()acceptnull.
An Eloquent user whose password column is NULL already flowed through those paths without a peep, because Eloquent's getAuthPassword() returns null for a missing attribute. GenericUser was the outlier: it read the attribute directly and warned. With this patch, a GenericUser without a password behaves exactly like an Eloquent user with a null password column — the final piece that makes the passwordless case consistent across user implementations.
Example: logging in a passwordless GenericUser
use Illuminate\Auth\GenericUser;
use Illuminate\Contracts\Auth\UserProvider;
class SsoUserProvider implements UserProvider
{
public function retrieveById($identifier)
{
// Accounts authenticated through an external provider
// have no local password credential.
return new GenericUser(['id' => $identifier]);
}
// ...
}
// In a controller or middleware:
Auth::login($provider->retrieveById($request->route('id')));
// v13.34.0: emits "Undefined array key 'password'" on every login.
// With #61876: no warning, and no password hash in the session:
session()->has('password_hash_web'); // false
If your own code consumes getAuthPassword() directly, the return value can now legitimately be null:
if (($hash = $user->getAuthPassword()) !== null) {
// the user has a local password credential
}
Upgrade impact
- No config, migration, or code changes required. The fix is drop-in for anyone on 13.34.0: update and the warnings stop.
- The only observable contract change is the return type.
getAuthPassword()may now returnnullfor aGenericUser(or a subclass) without the configured password attribute. Static analyzers such as PHPStan or Larastan may flag call sites that assumed astring— add a null check or?? ''where appropriate. - Users with passwords are unaffected. When the attribute exists, it is returned unchanged, and the session hash is stored exactly as before.
- Custom password names work the same way. If you override
getAuthPasswordName(), thenullfallback now applies to that key too.
Takeaways
GenericUser::getAuthPassword()now returnsnullwhen the password attribute is missing, instead of raising anUndefined array key "password"warning.- The warnings appeared because #61594 makes
SessionGuard::login()callgetAuthPassword()on every login to store a session password hash; passwordlessGenericUsers tripped a missing-key warning each time. - Passwordless logins are now silent and store no password hash, matching Eloquent users with a
nullpassword column. - Upgrading from v13.34.0 is safe: no configuration changes, but treat the return type as
string|nullif you callgetAuthPassword()in your own code.