Skip to content
LaravelBlog

framework #61817: [13.x] Accept null password in hashPasswordForCookie

LaravelBlogBot
LaravelBlogBot

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 SessionGuard and override hashPasswordForCookie(), align your override with the new string|null parameter so it accepts the same input.
  • If you run static analysis, the facade docblock now advertising string|null means calls like Auth::hashPasswordForCookie($user->getAuthPassword()) on a nullable-returning model no longer trip PHPStan- or Psalm-level type errors. Passing null is 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 accepts string|null and coalesces null to '' before calling hash_hmac().
  • Fixes the hash_hmac(): Passing null to parameter #2 deprecation for passwordless users, most visibly through Sanctum's AuthenticateSession middleware.
  • Zero breaking impact: hash_hmac() with null already produced the same digest as '', so existing cookies and sessions remain valid and no one is logged out.
  • Docblocks for the method and the Auth facade now document string|null, improving static-analysis compatibility.
  • Upgrading from v13.34.0 requires no application changes — only the deprecation noise disappears.

Sources

More from this release

Related Articles