framework #61817: [13.x] Accept null password in hashPasswordForCookie
Contributed by troioi-vn
Part of the framework v13.35.0 release
Category: Fix
Upgrading from v13.34.0 on PHP 8.1 or newer, PR #61817 quietly removes one of the more annoying deprecation notices in the auth layer: hash_hmac(): Passing null to parameter #2 ($data) of type string is deprecated. It fired whenever SessionGuard::hashPasswordForCookie() received a null password hash — which happens for every OAuth-only or magic-link user who returns null from getAuthPassword() and then passes through Sanctum's AuthenticateSession middleware. The fix is a one-line null-coalesce inside the method, and it changes nothing about the hashes the framework produces.
What Changed
SessionGuard::hashPasswordForCookie() now coalesces null to an empty string before hashing:
// Before (v13.34.0)
return hash_hmac('sha256', $passwordHash, $this->hashKey ?? 'base-key-for-password-hash-mac');
// After
return hash_hmac('sha256', $passwordHash ?? '', $this->hashKey ?? 'base-key-for-password-hash-mac');
Because the cast happens inside the method itself, every caller is covered in one place — Sanctum's middleware, the framework's own internals, and any userland code invoking it through the Auth facade. The method's @param docblock and the corresponding line in the Auth facade docblock were both updated from string to string|null, so the nullable input is now part of the documented contract rather than an accident.
Why It Matters
Passwordless authentication is increasingly common: Socialite-based OAuth accounts and magic-link logins often never set a password, so getAuthPassword() returns null. The framework already accommodated that in most places — userFromRecaller() rejects null passwords before hashing (see the existing testUserReturnsNullWhenRecallerUserHasNullPassword test), and Laravel's own session middleware skips password validation for these users entirely.
The gap was hashPasswordForCookie(). Sanctum's AuthenticateSession middleware calls it on every authenticated request to compare the stored "password fingerprint" against the current one, so null-password users triggered the deprecation on essentially every request. Under PHP 8.1+ those notices land in your logs, and if your tooling promotes deprecations to failures — PHPUnit's failOnDeprecation, strict CI deprecation handlers — it could break builds outright.
A Concrete Example
Given a passwordless user model:
class User extends Authenticatable
{
// Magic-link login: no password value is ever set.
public function getAuthPassword(): ?string
{
return null;
}
}
every authenticated request through AuthenticateSession produced:
PHP Deprecated: hash_hmac(): Passing null to parameter #2 ($data)
of type string is deprecated in
.../Illuminate/Auth/SessionGuard.php
After the patch, passing null behaves exactly like passing an empty string and raises nothing:
$guard->hashPasswordForCookie(null); // no deprecation
// identical output to:
$guard->hashPasswordForCookie('');
Upgrade Impact: No Breaking Changes
The key question for any patch touching password hashing is whether existing sessions survive. They do. PHP's deprecation for hash_hmac() is only a notice — it still coerced null to '' while complaining. In other words, hash_hmac('sha256', null, $key) already equaled hash_hmac('sha256', '', $key), so every cookie and session value written before this upgrade still validates afterwards. Nobody gets logged out, and the "password hash changed on another device" detection is unaffected. Only the noise goes away.
Practically, the upgrade path is: pull the new framework version and you're done — no code, config, or data changes. Two minor notes:
- If you subclass
SessionGuardand overridehashPasswordForCookie(), align your override with the newstring|nullparameter so it accepts the same input. - If you run static analysis, the facade docblock now advertising
string|nullmeans calls likeAuth::hashPasswordForCookie($user->getAuthPassword())on a nullable-returning model no longer trip PHPStan- or Psalm-level type errors. Passingnullis officially supported, not just tolerated.
How It's Verified
The PR adds testHashPasswordForCookieAcceptsNullPassword, which installs a temporary error handler collecting E_DEPRECATED messages, calls hashPasswordForCookie(null), and asserts both silence and hash equality:
$hash = $guard->hashPasswordForCookie(null);
$this->assertSame([], $deprecations);
$this->assertSame($guard->hashPasswordForCookie(''), $hash);
On 13.x the test fails with the exact deprecation users were seeing; with the change it passes. The full auth-related suites — tests/Auth, tests/Session, and tests/Integration/Auth — pass with 428 tests green.
Takeaways
SessionGuard::hashPasswordForCookie()now acceptsstring|nulland coalescesnullto''before callinghash_hmac().- Fixes the
hash_hmac(): Passing null to parameter #2deprecation for passwordless users, most visibly through Sanctum'sAuthenticateSessionmiddleware. - Zero breaking impact:
hash_hmac()withnullalready produced the same digest as'', so existing cookies and sessions remain valid and no one is logged out. - Docblocks for the method and the
Authfacade now documentstring|null, improving static-analysis compatibility. - Upgrading from v13.34.0 requires no application changes — only the deprecation noise disappears.