Skip to content

fix: verify the password once per panel sign-in - #20347

Draft
shaheenfawzy wants to merge 1 commit into
filamentphp:5.xfrom
shaheenfawzy:fix/login-duplicate-password-verification
Draft

fix: verify the password once per panel sign-in#20347
shaheenfawzy wants to merge 1 commit into
filamentphp:5.xfrom
shaheenfawzy:fix/login-duplicate-password-verification

Conversation

@shaheenfawzy

@shaheenfawzy shaheenfawzy commented Aug 7, 2026

Copy link
Copy Markdown

Description

Login::authenticate() verifies the submitted password twice on every successful sign-in.

The first Timebox retrieves the user and calls validateCredentials(), so by the time it returns the credentials are known to be good and the panel access check has already run:

$user = app(Timebox::class)->call(function (Timebox $timebox) use (...): ?Authenticatable {
    // retrieveByCredentials() + validateCredentials() + isUserAllowedToAccessPanel()
    return $user;
}, $timeboxDuration);

Those same credentials are then handed to the guard:

if (! $authGuard->attemptWhen($credentials, fn (Authenticatable $user): bool => $this->isUserAllowedToAccessPanel($user), $remember)) {

attemptWhen() runs retrieveByCredentials() and validateCredentials() from scratch, so every sign-in pays two bcrypt comparisons and two SELECTs, fires Attempting twice, and runs the panel access check twice. At a production bcrypt cost of 12 the redundant hash alone is around 200ms.

Since nothing is left for the guard to establish, the user is logged in directly with $authGuard->login(). The behaviour the guard would have contributed is kept:

  • SessionGuard::hasValidCredentials() dispatches Validated, and is protected, so Validated is now dispatched from authenticate() at the point the guard would have dispatched it.
  • SessionGuard::$rehashOnLogin is protected, so the rehash reads config('hashing.rehash_on_login', true) directly, which is the same value AuthManager passes to the guard when it constructs it.

One deliberate difference: SessionGuard::$lastAttempted is protected with no setter, so it is no longer populated by a panel login. Nothing in Filament reads it.

Reproduction: https://github.com/shaheenfawzy/filament-login-double-hash (composer setup && php artisan test --filter=LoginPerformanceTest)

The same method also pads every sign-in with the multi-factor timebox; that is a separate concern and is fixed in #20348.

Visual changes

None.

Functional changes

  • Code style has been fixed by running the composer cs command.
  • Changes have been tested to not break existing functionality.
  • Documentation is up-to-date.

tests/src/Panels/Auth/LoginTest.php gains three tests: Attempting and Validated each fire exactly once on a successful sign-in, a password stored at a lower bcrypt cost is rehashed, and it is left alone when hashing.rehash_on_login is disabled. The first fails on 5.x without this change, seeing Attempting twice. The two rehash tests pass either way and are regression guards, since attemptWhen() rehashed too.

@shaheenfawzy
shaheenfawzy force-pushed the fix/login-duplicate-password-verification branch from 24382d6 to e5f58a0 Compare August 7, 2026 18:14
`authenticate()` retrieved the user and verified the password inside the first
`Timebox`, then handed the same credentials to `attemptWhen()`, which retrieved
and verified them again. Every successful sign-in cost two bcrypt comparisons
and two `SELECT`s, and fired `Attempting` twice.

The first `Timebox` already establishes everything `attemptWhen()` re-checks,
including the panel access check, so the user is now logged in directly.
`Validated` is dispatched where the guard would have dispatched it, and the
password is still rehashed when `hashing.rehash_on_login` allows it.

At a bcrypt cost of 12 this takes roughly 200ms off every sign-in.
@shaheenfawzy
shaheenfawzy force-pushed the fix/login-duplicate-password-verification branch from e5f58a0 to cdaa304 Compare August 7, 2026 18:41
@shaheenfawzy shaheenfawzy changed the title Verify the password once per panel sign-in fix: verify the password once per panel sign-in Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant