Support HMAC password hash format from Laravel 12.45.0+#578
Conversation
Laravel Framework v12.45.0 (PR laravel/framework#58107) changed how password hashes are stored in sessions - they're now stored as HMACs instead of raw hashes for improved security. This updates Sanctum's AuthenticateSession middleware to: 1. Use hashPasswordForCookie() when storing the password hash (if available) 2. Add validatePasswordHash() that tries HMAC format first, falls back to raw hash comparison for backward compatibility This ensures compatibility when both $middleware->authenticateSessions() and Sanctum stateful auth are enabled together. Fixes laravel#577
|
Hello, This PR addresses the HMAC hash compatibility, which is a great improvement for security. I wanted to share a related issue and a helpful workaround for users implementing passwordless authentication (such as magic links or OTP), where the user record in the database has a In a passwordless setup, you might still encounter issues with To prevent potential errors (like a Here is the custom middleware to use instead of the default <?php
// ...
final class AuthenticateSession extends SanctumAuthenticateSession
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure(Request): Response $next
* @return Response
* @throws AuthenticationException
*/
public function handle(Request $request, Closure $next): Response
{
if ($this->shouldSkipAuthentication($request)) {
return $next($request);
}
return parent::handle($request, $next);
}
/**
* Determine if the authentication should be skipped.
*
* Skip when the user is authenticated but has no password (passwordless users).
*/
private function shouldSkipAuthentication(Request $request): bool
{
$user = $request->user();
if ($user === null) {
return false;
}
return $user->getAuthPassword() === null || $user->getAuthPassword() === '';
}
}This ensures that session validation is correctly skipped when the authenticated user is intentionally passwordless. |
| * @param string $storedValue | ||
| * @return bool | ||
| */ | ||
| protected function validatePasswordHash(SessionGuard $guard, string $passwordHash, string $storedValue): bool |
There was a problem hiding this comment.
$passwordHash may be null here.
Laravel\Sanctum\Http\Middleware\AuthenticateSession::validatePasswordHash(): Argument #2 ($passwordHash) must be of type string, null given, called in vendor/laravel/sanctum/src/Http/Middleware/AuthenticateSession.php on line 53
There was a problem hiding this comment.
Update: My user factory wasn't setting a password so some of my tests were failing with the above error.
As @rdehnhardt mentioned there are cases where password is null for real world users so perhaps the $passwordHash type should be ?string or have the type removed. Both SessionGuard::hashPasswordForCookie() and hash_equals do not enforce the string type.
@taylorotwell I've opened a PR #581 to address this.
In my case I've just set a password in my factory.
|
Why would you not just extend it? Just asking. Setting this to ?string just seems like a bad idea all around.
__________________________
Ryan Olson
Co-founder / CEO
Arctic Media Solutions Inc.
Sent from my mobile
________________________________
From: patrickomeara ***@***.***>
Sent: Saturday, January 10, 2026 9:24:38 PM
To: laravel/sanctum ***@***.***>
Cc: Ryan Olson ***@***.***>; Author ***@***.***>
Subject: Re: [laravel/sanctum] Support HMAC password hash format from Laravel 12.45.0+ (PR #578)
@patrickomeara commented on this pull request.
________________________________
In src/Http/Middleware/AuthenticateSession.php<#578 (comment)>:
]);
}
+
+ /**
+ * Validate the password hash against the stored value.
+ *
+ * @param \Illuminate\Auth\SessionGuard $guard
+ * @param string $passwordHash
+ * @param string $storedValue
+ * @return bool
+ */
+ protected function validatePasswordHash(SessionGuard $guard, string $passwordHash, string $storedValue): bool
Update: My user factory wasn't setting a password so some of my tests were failing with the above error.
As @rdehnhardt<https://github.com/rdehnhardt> mentioned there are cases where password is null for real world users so perhaps the $passwordHash type should be ?string or have the type removed. Both SessionGuard::hashPasswordForCookie() and hash_equals do not enforce the string type.
In my case I've just set a password in my factory.
@taylorotwell<https://github.com/taylorotwell> I've opened a PR #581<#581> to address this.
—
Reply to this email directly, view it on GitHub<#578 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/ALB44JJ3RFENH3FYNPYGVZD4GGX6NAVCNFSM6AAAAACQ33WKOKVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZTMNBXGM3DAMRSGM>.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
Summary
Laravel Framework v12.45.0 (PR laravel/framework#58107) changed how password hashes are stored in sessions - they're now stored as HMACs instead of raw hashes for improved security.
This updates Sanctum's
AuthenticateSessionmiddleware to match the framework's approach.Changes
storePasswordHashInSession()- UseshashPasswordForCookie()when available (Laravel 12.45.0+), falls back to raw hash for older versionsvalidatePasswordHash()- New method that tries HMAC format first, falls back to raw hash comparison for backward compatibilityProblem
When both
$middleware->authenticateSessions()and Sanctum stateful auth are enabled:AuthenticateSessionstorespassword_hash_webas HMACAuthenticateSessioncompares against raw hash → mismatch → 401Backward Compatibility
method_exists()checks so it works with Laravel versions before and after 12.45.0Related