Skip to content

Commit 1c8bd69

Browse files
authored
Merge pull request #764 from cakephp/feature/primary-key-session-default-identifier
Add default TokenIdentifier for PrimaryKeySessionAuthenticator
2 parents 7aa5be8 + ef15407 commit 1c8bd69

File tree

3 files changed

+154
-18
lines changed

3 files changed

+154
-18
lines changed

docs/en/authenticators.rst

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,34 @@ It also helps to avoid session invalidation.
3737
Session itself stores the entity object including nested objects like DateTime or enums.
3838
With only the ID stored, the invalidation due to objects being modified will also dissolve.
3939

40-
Make sure to match this with a Token identifier with ``key``/``id`` keys::
40+
A default ``TokenIdentifier`` is provided that looks up users by their ``id`` field,
41+
so minimal configuration is required::
42+
43+
$service->loadAuthenticator('Authentication.PrimaryKeySession');
44+
45+
Configuration options:
46+
47+
- **idField**: The field in the database table to look up. Default is ``id``.
48+
- **identifierKey**: The key used to store/retrieve the primary key from session data.
49+
Default is ``key``.
50+
51+
For custom lookup fields, the ``idField`` and ``identifierKey`` options propagate
52+
to the default identifier automatically::
53+
54+
$service->loadAuthenticator('Authentication.PrimaryKeySession', [
55+
'idField' => 'uuid',
56+
]);
57+
58+
You can also provide a fully custom identifier configuration if needed::
4159

4260
$service->loadAuthenticator('Authentication.PrimaryKeySession', [
4361
'identifier' => [
4462
'Authentication.Token' => [
45-
'tokenField' => 'id', // lookup for resolver and DB table
46-
'dataField' => 'key', // incoming data from authenticator
63+
'tokenField' => 'id',
64+
'dataField' => 'key',
4765
'resolver' => 'Authentication.Orm',
4866
],
4967
],
50-
'urlChecker' => 'Authentication.CakeRouter',
51-
'loginUrl' => [
52-
'prefix' => false,
53-
'plugin' => false,
54-
'controller' => 'Users',
55-
'action' => 'login',
56-
],
5768
]);
5869

5970
Form

src/Authenticator/PrimaryKeySessionAuthenticator.php

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,95 @@
44
namespace Authentication\Authenticator;
55

66
use ArrayAccess;
7+
use Authentication\Identifier\IdentifierFactory;
78
use Authentication\Identifier\IdentifierInterface;
89
use Cake\Http\Exception\UnauthorizedException;
910
use Psr\Http\Message\ResponseInterface;
1011
use Psr\Http\Message\ServerRequestInterface;
1112

1213
/**
1314
* Session Authenticator with only ID
15+
*
16+
* This authenticator stores only the user's primary key in the session,
17+
* and looks up the full user record from the database on each request.
18+
*
19+
* By default, it uses a TokenIdentifier configured to look up users by
20+
* their `id` field. This works out of the box for most applications:
21+
*
22+
* ```php
23+
* $service->loadAuthenticator('Authentication.PrimaryKeySession');
24+
* ```
25+
*
26+
* You can customize the identifier configuration if needed:
27+
*
28+
* ```php
29+
* $service->loadAuthenticator('Authentication.PrimaryKeySession', [
30+
* 'identifier' => [
31+
* 'className' => 'Authentication.Token',
32+
* 'tokenField' => 'uuid',
33+
* 'dataField' => 'key',
34+
* 'resolver' => [
35+
* 'className' => 'Authentication.Orm',
36+
* 'userModel' => 'Members',
37+
* ],
38+
* ],
39+
* ]);
40+
* ```
1441
*/
1542
class PrimaryKeySessionAuthenticator extends SessionAuthenticator
1643
{
1744
/**
18-
* @param \Authentication\Identifier\IdentifierInterface|null $identifier
19-
* @param array<string, mixed> $config
45+
* Default config for this object.
46+
*
47+
* - `identifierKey` The key used when passing the ID to the identifier.
48+
* - `idField` The field on the user entity that contains the primary key.
49+
*
50+
* @var array<string, mixed>
51+
*/
52+
protected array $_defaultConfig = [
53+
'fields' => [],
54+
'sessionKey' => 'Auth',
55+
'impersonateSessionKey' => 'AuthImpersonate',
56+
'identify' => false,
57+
'identityAttribute' => 'identity',
58+
'identifierKey' => 'key',
59+
'idField' => 'id',
60+
];
61+
62+
/**
63+
* Constructor
64+
*
65+
* Bypasses SessionAuthenticator's default PasswordIdentifier creation
66+
* to allow lazy initialization of the TokenIdentifier in getIdentifier().
67+
*
68+
* @param \Authentication\Identifier\IdentifierInterface|null $identifier Identifier instance.
69+
* @param array<string, mixed> $config Configuration settings.
2070
*/
2171
public function __construct(?IdentifierInterface $identifier, array $config = [])
2272
{
23-
$config += [
24-
'identifierKey' => 'key',
25-
'idField' => 'id',
26-
];
73+
$this->_identifier = $identifier;
74+
$this->setConfig($config);
75+
}
76+
77+
/**
78+
* Gets the identifier.
79+
*
80+
* If no identifier was explicitly configured, creates a default TokenIdentifier
81+
* configured to look up users by their primary key (`id` field).
82+
*
83+
* @return \Authentication\Identifier\IdentifierInterface
84+
*/
85+
public function getIdentifier(): IdentifierInterface
86+
{
87+
if ($this->_identifier === null) {
88+
$this->_identifier = IdentifierFactory::create([
89+
'className' => 'Authentication.Token',
90+
'tokenField' => $this->getConfig('idField'),
91+
'dataField' => $this->getConfig('identifierKey'),
92+
]);
93+
}
2794

28-
parent::__construct($identifier, $config);
95+
return $this->_identifier;
2996
}
3097

3198
/**

tests/TestCase/Authenticator/PrimaryKeySessionAuthenticatorTest.php

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Authentication\Authenticator\PrimaryKeySessionAuthenticator;
2121
use Authentication\Authenticator\Result;
2222
use Authentication\Identifier\IdentifierFactory;
23+
use Authentication\Identifier\TokenIdentifier;
2324
use Cake\Http\Exception\UnauthorizedException;
2425
use Cake\Http\Response;
2526
use Cake\Http\ServerRequestFactory;
@@ -67,7 +68,7 @@ public function setUp(): void
6768
}
6869

6970
/**
70-
* Test authentication
71+
* Test authentication with explicit identifier
7172
*
7273
* @return void
7374
*/
@@ -89,6 +90,63 @@ public function testAuthenticateSuccess()
8990
$this->assertSame(Result::SUCCESS, $result->getStatus());
9091
}
9192

93+
/**
94+
* Test authentication works with default identifier (no explicit configuration)
95+
*
96+
* @return void
97+
*/
98+
public function testAuthenticateSuccessWithDefaultIdentifier()
99+
{
100+
$request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/']);
101+
102+
$this->sessionMock->expects($this->once())
103+
->method('read')
104+
->with('Auth')
105+
->willReturn(1);
106+
107+
$request = $request->withAttribute('session', $this->sessionMock);
108+
109+
// No identifier passed - should use the default TokenIdentifier
110+
$authenticator = new PrimaryKeySessionAuthenticator(null);
111+
$result = $authenticator->authenticate($request);
112+
113+
$this->assertInstanceOf(Result::class, $result);
114+
$this->assertSame(Result::SUCCESS, $result->getStatus());
115+
}
116+
117+
/**
118+
* Test getIdentifier returns default TokenIdentifier when none configured
119+
*
120+
* @return void
121+
*/
122+
public function testGetIdentifierReturnsDefaultWhenNotConfigured()
123+
{
124+
$authenticator = new PrimaryKeySessionAuthenticator(null);
125+
$identifier = $authenticator->getIdentifier();
126+
127+
$this->assertInstanceOf(TokenIdentifier::class, $identifier);
128+
$this->assertSame('id', $identifier->getConfig('tokenField'));
129+
$this->assertSame('key', $identifier->getConfig('dataField'));
130+
}
131+
132+
/**
133+
* Test custom idField/identifierKey config propagates to default identifier
134+
*
135+
* @return void
136+
*/
137+
public function testGetIdentifierUsesCustomConfig()
138+
{
139+
$authenticator = new PrimaryKeySessionAuthenticator(null, [
140+
'idField' => 'uuid',
141+
'identifierKey' => 'token',
142+
]);
143+
$identifier = $authenticator->getIdentifier();
144+
145+
$this->assertInstanceOf(TokenIdentifier::class, $identifier);
146+
$this->assertSame('uuid', $identifier->getConfig('tokenField'));
147+
$this->assertSame('token', $identifier->getConfig('dataField'));
148+
}
149+
92150
/**
93151
* Test authentication
94152
*

0 commit comments

Comments
 (0)