Skip to content

Commit 9d65b3d

Browse files
committed
minor #20636 [Security] Update custom authenticator docs to include identifier normalization (Spomky)
This PR was merged into the 7.3 branch. Discussion ---------- [Security] Update custom authenticator docs to include identifier normalization Fixes #20632 Improve the documentation for custom authenticators by detailing the concept of user identifier normalization. Introduced the `NormalizedUserBadge` example to demonstrate how to simplify and standardize identifiers. Commits ------- ac6a258 Introduce user identifier normalization for enhanced consistency
2 parents b17f770 + ac6a258 commit 9d65b3d

File tree

1 file changed

+95
-3
lines changed

1 file changed

+95
-3
lines changed

security/custom_authenticator.rst

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,20 @@ requires a user and some sort of "credentials" (e.g. a password).
209209
Use the
210210
:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Badge\\UserBadge`
211211
to attach the user to the passport. The ``UserBadge`` requires a user
212-
identifier (e.g. the username or email), which is used to load the user
213-
using :ref:`the user provider <security-user-providers>`::
212+
identifier (e.g. the username or email)::
214213

215214
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
216215

217216
// ...
218-
$passport = new Passport(new UserBadge($email), $credentials);
217+
$passport = new Passport(new UserBadge($userIdentifier), $credentials);
218+
219+
User Identifier
220+
~~~~~~~~~~~~~~~
221+
222+
The user identifier is a unique string that identifies the user. It is used
223+
to load the user using :ref:`the user provider <security-user-providers>`.
224+
This identifier is often something like the user's email address or username,
225+
but it could be any unique value associated with the user.
219226

220227
.. note::
221228

@@ -255,6 +262,91 @@ using :ref:`the user provider <security-user-providers>`::
255262
}
256263
}
257264

265+
It is a good practice to normalize the user identifier before using it.
266+
For example, this ensures that variations such as "john.doe", "John.Doe",
267+
or "JOHN.DOE" refer to the same user.
268+
Normalization can include converting the identifier to lowercase
269+
and trimming unnecessary spaces.
270+
You can optionally pass a user identifier normalizer as third argument to the
271+
``UserBadge``. This callable receives the ``$userIdentifier``
272+
and must return a normalized user identifier as a string.
273+
274+
.. versionadded:: 7.3
275+
276+
The support of the user identifier normalizer was introduced in Symfony 7.3.
277+
278+
For instance, the example below uses a normalizer that converts usernames to a normalized, ASCII-only, lowercase format,
279+
suitable for consistent comparison and storage.
280+
281+
// src/Security/NormalizedUserBadge.php
282+
namespace App\Security;
283+
284+
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
285+
use Symfony\Component\String\UnicodeString;
286+
use function Symfony\Component\String\u;
287+
288+
final class NormalizedUserBadge extends UserBadge
289+
{
290+
public function __construct(string $identifier)
291+
{
292+
$callback = static fn (string $identifier) => u($identifier)->normalize(UnicodeString::NFKC)->ascii()->lower()->toString();
293+
294+
parent::__construct($identifier, null, $callback);
295+
}
296+
}
297+
298+
// src/Security/PasswordAuthenticator.php
299+
namespace App\Security;
300+
301+
final class PasswordAuthenticator extends AbstractLoginFormAuthenticator
302+
{
303+
// Simplified for brievety
304+
public function authenticate(Request $request): Passport
305+
{
306+
$username = (string) $request->request->get('username', '');
307+
$password = (string) $request->request->get('password', '');
308+
309+
$request->getSession()
310+
->set(SecurityRequestAttributes::LAST_USERNAME, $username);
311+
312+
return new Passport(
313+
new NormalizedUserBadge($username),
314+
new PasswordCredentials($password),
315+
[
316+
//All other useful badges
317+
]
318+
);
319+
}
320+
}
321+
322+
.. note::
323+
324+
For example, Google treats the following email addresses as equivalent:
325+
``john.doe@gmail.com``, ``j.hon.d.oe@gmail.com``, and ``johndoe@gmail.com``.
326+
This is because Google applies normalization rules that remove dots
327+
and convert the address to lowercase (though behavior varies across services).
328+
329+
.. note::
330+
331+
In enterprise environments, a user may authenticate using different formats
332+
of their identifier, such as:
333+
334+
- ``john.doe@acme.com``
335+
- ``acme.com\jdoe``
336+
- ``https://acme.com/+jdoe``
337+
- ``acct:jdoe@acme.com``
338+
339+
Applying normalization (e.g., trimming, lowercasing, or format unification)
340+
helps ensure consistent identity recognition across systems and prevents
341+
duplicates caused by format variations.
342+
343+
User Credential
344+
~~~~~~~~~~~~~~~
345+
346+
The user credential is used to authenticate the user i.e. to verify
347+
the validity of the provided information (such as a password, an API token,
348+
or other custom credentials).
349+
258350
The following credential classes are supported by default:
259351

260352
:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Credentials\\PasswordCredentials`

0 commit comments

Comments
 (0)