Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions docs/en/authenticators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=================
Expand Down
26 changes: 11 additions & 15 deletions docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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
</identifiers>` 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 </identifiers>` to convert the credentials users will give us into an
:doc:`identity </identity-object>` which represents our logged in user.

If one of the configured authenticators was able to validate the credentials,
Expand Down
14 changes: 11 additions & 3 deletions src/Authenticator/SessionAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
*/
Expand Down Expand Up @@ -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];
Expand Down
45 changes: 27 additions & 18 deletions tests/TestCase/AuthenticationServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
});
}

/**
Expand Down
29 changes: 23 additions & 6 deletions tests/TestCase/Authenticator/SessionAuthenticatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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())
Expand All @@ -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())
Expand All @@ -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());
});
}

/**
Expand Down