From ad2c520fcd907226b9a237acd4a3e3065c5f2d53 Mon Sep 17 00:00:00 2001 From: mscherer Date: Tue, 13 Jan 2026 18:06:02 +0100 Subject: [PATCH] Deprecate SessionAuthenticator identify option The `identify` option only verifies that the username exists in the database, it does not verify passwords as the documentation previously suggested. This makes it ineffective for the use case of detecting password changes or remotely invalidating sessions. Users should use `PrimaryKeySessionAuthenticator` instead if they need to fetch fresh user data from the database on each request. Also updated the Quick Start guide to clarify that SessionAuthenticator does not need an identifier - it uses session data directly as identity. --- docs/en/authenticators.rst | 11 +++-- docs/en/index.rst | 26 +++++------ src/Authenticator/SessionAuthenticator.php | 14 ++++-- tests/TestCase/AuthenticationServiceTest.php | 45 +++++++++++-------- .../SessionAuthenticatorTest.php | 29 +++++++++--- 5 files changed, 77 insertions(+), 48 deletions(-) diff --git a/docs/en/authenticators.rst b/docs/en/authenticators.rst index a3bfc1b3..7ea4ff58 100644 --- a/docs/en/authenticators.rst +++ b/docs/en/authenticators.rst @@ -18,13 +18,12 @@ Configuration options: - **sessionKey**: The session key for the user data, default is ``Auth`` -- **identify**: Set this key with a value of bool ``true`` to enable checking - the session credentials against the identifiers. When ``true``, the configured - :doc:`/identifiers` are used to identify the user using data - stored in the session on each request. Default value is ``false``. +- **identify**: Deprecated in 3.4.0. This option only verifies that the + username exists in the database, it does not verify passwords as the + documentation previously suggested. Use ``PrimaryKeySessionAuthenticator`` + instead if you need to fetch fresh user data from the database on each request. - **fields**: Allows you to map the ``username`` field to the unique - identifier in your user storage. Defaults to ``username``. This option is - used when the ``identify`` option is set to true. + identifier in your user storage. Defaults to ``username``. PrimaryKeySession ================= diff --git a/docs/en/index.rst b/docs/en/index.rst index 4f12ea25..01f22317 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -32,7 +32,6 @@ imports:: use Authentication\AuthenticationServiceInterface; use Authentication\AuthenticationServiceProviderInterface; use Authentication\Identifier\AbstractIdentifier; - use Authentication\Identifier\IdentifierInterface; use Authentication\Middleware\AuthenticationMiddleware; use Cake\Http\MiddlewareQueue; use Cake\Routing\Router; @@ -94,23 +93,20 @@ define the ``AuthenticationService`` it wants to use. Add the following method t 'queryParam' => 'redirect', ]); - // Define identifiers $fields = [ AbstractIdentifier::CREDENTIAL_USERNAME => 'email', - AbstractIdentifier::CREDENTIAL_PASSWORD => 'password' - ]; - $passwordIdentifier = [ - 'Authentication.Password' => [ - 'fields' => $fields, - ], + AbstractIdentifier::CREDENTIAL_PASSWORD => 'password', ]; // Load the authenticators. Session should be first. - $service->loadAuthenticator('Authentication.Session', [ - 'identifier' => $passwordIdentifier, - ]); + // Session just uses session data directly as identity, no identifier needed. + $service->loadAuthenticator('Authentication.Session'); $service->loadAuthenticator('Authentication.Form', [ - 'identifier' => $passwordIdentifier, + 'identifier' => [ + 'Authentication.Password' => [ + 'fields' => $fields, + ], + ], 'fields' => $fields, 'loginUrl' => Router::url([ 'prefix' => false, @@ -126,9 +122,9 @@ define the ``AuthenticationService`` it wants to use. Add the following method t First, we configure what to do with users when they are not authenticated. Next, we attach the ``Session`` and ``Form`` :doc:`/authenticators` which define the mechanisms that our application will use to authenticate users. ``Session`` enables us to identify -users based on data in the session while ``Form`` enables us -to handle a login form at the ``loginUrl``. Finally we attach an :doc:`identifier -` to convert the credentials users will give us into an +users based on data in the session - it uses the session data directly as identity without any +database lookup. ``Form`` enables us to handle a login form at the ``loginUrl`` and uses an +:doc:`identifier ` to convert the credentials users will give us into an :doc:`identity ` which represents our logged in user. If one of the configured authenticators was able to validate the credentials, diff --git a/src/Authenticator/SessionAuthenticator.php b/src/Authenticator/SessionAuthenticator.php index a6c98dea..00ec5b37 100644 --- a/src/Authenticator/SessionAuthenticator.php +++ b/src/Authenticator/SessionAuthenticator.php @@ -21,6 +21,7 @@ use Cake\Http\Exception\UnauthorizedException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use function Cake\Core\deprecationWarning; /** * Session Authenticator @@ -31,9 +32,10 @@ class SessionAuthenticator extends AbstractAuthenticator implements PersistenceI * Default config for this object. * - `fields` The fields to use to verify a user by. * - `sessionKey` Session key. - * - `identify` Whether to identify user data stored in a session. This is - * useful if you want to remotely end sessions that have a different password stored, - * or if your identification logic needs additional conditions before a user can login. + * - `identify` Whether to identify user data stored in a session. + * Deprecated: This option only verifies that the username exists in the database, + * it does not verify passwords. Use `PrimaryKeySessionAuthenticator` instead if you + * need to fetch fresh user data from the database on each request. * * @var array */ @@ -65,6 +67,12 @@ public function authenticate(ServerRequestInterface $request): ResultInterface } if ($this->getConfig('identify') === true) { + deprecationWarning( + '3.4.0', + 'The `identify` option is deprecated. ' . + 'This option only verifies that the username exists, not the password. ' . + 'Use `PrimaryKeySessionAuthenticator` instead to fetch fresh user data on each request.', + ); $credentials = []; foreach ($this->getConfig('fields') as $key => $field) { $credentials[$key] = $user[$field]; diff --git a/tests/TestCase/AuthenticationServiceTest.php b/tests/TestCase/AuthenticationServiceTest.php index 0a11738d..2b96b8b8 100644 --- a/tests/TestCase/AuthenticationServiceTest.php +++ b/tests/TestCase/AuthenticationServiceTest.php @@ -172,9 +172,15 @@ public function testAuthenticateWithChallengeDisabled() * Integration test for session auth + identify always getting a fresh user record. * * @return void + * @deprecated The `identify` option is deprecated. */ public function testAuthenticationWithSessionIdentify() { + $this->skipIf( + version_compare(Version::id(), '11.0', '<'), + 'For some reason PHPUnit doesn\'t pick up the deprecation on v10', + ); + $users = $this->fetchTable('Users'); $user = $users->get(1); @@ -197,25 +203,28 @@ public function testAuthenticationWithSessionIdentify() ], ]); }; - $service = $factory(); - $result = $service->authenticate($request); - $this->assertTrue($result->isValid()); - - $dateValue = new DateTime('2022-01-01 10:11:12'); - $identity = $result->getData(); - $this->assertEquals($identity->username, $user->username); - $this->assertNotEquals($identity->created, $dateValue); - - // Update the user so that we can ensure session is reading from the db. - $user->created = $dateValue; - $users->saveOrFail($user); - $service = $factory(); - $result = $service->authenticate($request); - $this->assertTrue($result->isValid()); - $identity = $result->getData(); - $this->assertEquals($identity->username, $user->username); - $this->assertEquals($identity->created, $dateValue); + $this->deprecated(function () use ($factory, $request, $users, $user) { + $service = $factory(); + $result = $service->authenticate($request); + $this->assertTrue($result->isValid()); + + $dateValue = new DateTime('2022-01-01 10:11:12'); + $identity = $result->getData(); + $this->assertEquals($identity->username, $user->username); + $this->assertNotEquals($identity->created, $dateValue); + + // Update the user so that we can ensure session is reading from the db. + $user->created = $dateValue; + $users->saveOrFail($user); + + $service = $factory(); + $result = $service->authenticate($request); + $this->assertTrue($result->isValid()); + $identity = $result->getData(); + $this->assertEquals($identity->username, $user->username); + $this->assertEquals($identity->created, $dateValue); + }); } /** diff --git a/tests/TestCase/Authenticator/SessionAuthenticatorTest.php b/tests/TestCase/Authenticator/SessionAuthenticatorTest.php index bd22d5ea..a2590200 100644 --- a/tests/TestCase/Authenticator/SessionAuthenticatorTest.php +++ b/tests/TestCase/Authenticator/SessionAuthenticatorTest.php @@ -27,6 +27,7 @@ use Cake\Http\ServerRequestFactory; use Cake\Http\Session; use Cake\ORM\TableRegistry; +use PHPUnit\Runner\Version; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -203,9 +204,15 @@ public function testAuthenticateFailure() * Test successful session data verification by database lookup * * @return void + * @deprecated The `identify` option is deprecated. */ public function testVerifyByDatabaseSuccess() { + $this->skipIf( + version_compare(Version::id(), '11.0', '<'), + 'For some reason PHPUnit doesn\'t pick up the deprecation on v10', + ); + $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/']); $this->sessionMock->expects($this->once()) @@ -221,19 +228,27 @@ public function testVerifyByDatabaseSuccess() $authenticator = new SessionAuthenticator($this->identifiers, [ 'identify' => true, ]); - $result = $authenticator->authenticate($request); + $this->deprecated(function () use ($authenticator, $request) { + $result = $authenticator->authenticate($request); - $this->assertInstanceOf(Result::class, $result); - $this->assertSame(Result::SUCCESS, $result->getStatus()); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(Result::SUCCESS, $result->getStatus()); + }); } /** * Test session data verification by database lookup failure * * @return void + * @deprecated The `identify` option is deprecated. */ public function testVerifyByDatabaseFailure() { + $this->skipIf( + version_compare(Version::id(), '11.0', '<'), + 'For some reason PHPUnit doesn\'t pick up the deprecation on v10', + ); + $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/']); $this->sessionMock->expects($this->once()) @@ -249,10 +264,12 @@ public function testVerifyByDatabaseFailure() $authenticator = new SessionAuthenticator($this->identifiers, [ 'identify' => true, ]); - $result = $authenticator->authenticate($request); + $this->deprecated(function () use ($authenticator, $request) { + $result = $authenticator->authenticate($request); - $this->assertInstanceOf(Result::class, $result); - $this->assertSame(Result::FAILURE_CREDENTIALS_INVALID, $result->getStatus()); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(Result::FAILURE_CREDENTIALS_INVALID, $result->getStatus()); + }); } /**