From 96a7f2fad995a5ca3aec8d6dd359b7d9fdd3b1e1 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:20:32 +0100 Subject: [PATCH 01/35] =?UTF-8?q?user=5Fauthentication.md:=20fixes,=20enco?= =?UTF-8?q?ders=20=E2=86=92=20password=5Fhashers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/users/user_authentication.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index d2b7280301..e534b230a2 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -52,9 +52,15 @@ security: users: # You will then be able to login with username "user" and password "userpass" user: { password: userpass, roles: [ 'ROLE_USER' ] } - # The "in memory" provider requires an encoder for Symfony\Component\Security\Core\User\User - encoders: - Symfony\Component\Security\Core\User\User: plaintext + password_hashers: + # The "in memory" provider requires an encoder + Symfony\Component\Security\Core\User\InMemoryUser: plaintext + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' + firewalls: + ibexa_front: + pattern: ^/ + provider: chain_provider + # … ``` ### Implement the listener From f2d32a3d2b721a0ade43da3cc5ad5f7dfea22c18 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 08:39:49 +0100 Subject: [PATCH 02/35] user_authentication.md: fix subscriber --- docs/users/user_authentication.md | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index e534b230a2..174a1aad3d 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -50,8 +50,8 @@ security: in_memory: memory: users: - # You will then be able to login with username "user" and password "userpass" - user: { password: userpass, roles: [ 'ROLE_USER' ] } + # You will then be able to log in with username "from_memory_user" and password "from_memory_pass" + from_memory_user: { password: from_memory_pass, roles: [ 'ROLE_USER' ] } password_hashers: # The "in memory" provider requires an encoder Symfony\Component\Security\Core\User\InMemoryUser: plaintext @@ -69,10 +69,10 @@ In the `config/services.yaml` file: ``` yaml services: - App\EventListener\InteractiveLoginListener: + App\EventSubscriber\InteractiveLoginSubscriber: arguments: ['@ibexa.api.service.user'] tags: - - { name: kernel.event_subscriber }  + - { name: kernel.event_subscriber } ``` Don't mix `MVCEvents::INTERACTIVE_LOGIN` event (specific to [[= product_name =]]) and `SecurityEvents::INTERACTIVE_LOGIN` event (fired by Symfony security component). @@ -80,37 +80,37 @@ Don't mix `MVCEvents::INTERACTIVE_LOGIN` event (specific to [[= product_name =]] ``` php userService = $userService; } public static function getSubscribedEvents() { return [ - MVCEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin' + SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin' ]; } public function onInteractiveLogin(InteractiveLoginEvent $event) { - // This loads a generic User and assigns it back to the event. - // You may want to create Users here, or even load predefined Users depending on your own rules. - $event->setApiUser($this->userService->loadUserByLogin( 'lolautruche' )); + $userMap = [ + 'from_memory_user' => 'generic_customer_account', + ]; + $userLogin = $userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; + $ibexaUser = $this->userService->loadUserByLogin($userLogin); + $event->getAuthenticationToken()->setUser(new User($ibexaUser)); + + return $event; } -}  +} ``` From d6d7933edfdda3015e61c995e42a47d66dfcd582 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:31:09 +0100 Subject: [PATCH 03/35] user_authentication.md: move code to external files --- .../in_memory/config/packages/security.yaml | 36 ++++++++++ .../in_memory/config/services.yaml | 5 ++ .../InteractiveLoginSubscriber.php | 35 ++++++++++ docs/users/user_authentication.md | 65 +------------------ 4 files changed, 79 insertions(+), 62 deletions(-) create mode 100644 code_samples/user_management/in_memory/config/packages/security.yaml create mode 100644 code_samples/user_management/in_memory/config/services.yaml create mode 100644 code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php diff --git a/code_samples/user_management/in_memory/config/packages/security.yaml b/code_samples/user_management/in_memory/config/packages/security.yaml new file mode 100644 index 0000000000..42fafb542b --- /dev/null +++ b/code_samples/user_management/in_memory/config/packages/security.yaml @@ -0,0 +1,36 @@ +security: + password_hashers: + # The in-memory provider requires an encoder + Symfony\Component\Security\Core\User\InMemoryUser: plaintext + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' + + # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded + providers: + ibexa: + id: ibexa.security.user_provider + in_memory: + memory: + users: + # You will then be able to log in with username "from_memory_user" and password "from_memory_pass" + from_memory_user: { password: from_memory_pass, roles: [ 'ROLE_USER' ] } + # Chaining in_memory and ibexa user providers + chain_provider: + chain: + providers: [ in_memory, ibexa ] + + firewalls: + # … + ibexa_front: + pattern: ^/ + provider: chain_provider + user_checker: Ibexa\Core\MVC\Symfony\Security\UserChecker + context: ibexa + form_login: + enable_csrf: true + login_path: login + check_path: login_check + custom_authenticators: + - Ibexa\PageBuilder\Security\EditorialMode\FragmentAuthenticator + entry_point: form_login + logout: + path: logout diff --git a/code_samples/user_management/in_memory/config/services.yaml b/code_samples/user_management/in_memory/config/services.yaml new file mode 100644 index 0000000000..5deab6f8cb --- /dev/null +++ b/code_samples/user_management/in_memory/config/services.yaml @@ -0,0 +1,5 @@ +services: + App\EventSubscriber\InteractiveLoginSubscriber: + arguments: ['@ibexa.api.service.user'] + tags: + - { name: kernel.event_subscriber } diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php new file mode 100644 index 0000000000..b5128ead24 --- /dev/null +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -0,0 +1,35 @@ + 'onInteractiveLogin' + ]; + } + + public function onInteractiveLogin(InteractiveLoginEvent $event) + { + $userMap = [ + 'from_memory_user' => 'generic_customer_account', + ]; + $userLogin = $userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; + $ibexaUser = $this->userService->loadUserByLogin($userLogin); + $event->getAuthenticationToken()->setUser(new User($ibexaUser)); + + return $event; + } +} diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index 174a1aad3d..d3f63d0687 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -39,28 +39,7 @@ The following is an example of using the in-memory user provider: ``` yaml # config/packages/security.yaml -security: - providers: - # Chaining in_memory and ibexa user providers - chain_provider: - chain: - providers: [in_memory, ibexa] - ibexa: - id: ibexa.security.user_provider - in_memory: - memory: - users: - # You will then be able to log in with username "from_memory_user" and password "from_memory_pass" - from_memory_user: { password: from_memory_pass, roles: [ 'ROLE_USER' ] } - password_hashers: - # The "in memory" provider requires an encoder - Symfony\Component\Security\Core\User\InMemoryUser: plaintext - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' - firewalls: - ibexa_front: - pattern: ^/ - provider: chain_provider - # … +[[= include_file('code_samples/user_management/in_memory/config/packages/security.yaml') =]] ``` ### Implement the listener @@ -68,49 +47,11 @@ security: In the `config/services.yaml` file: ``` yaml -services: - App\EventSubscriber\InteractiveLoginSubscriber: - arguments: ['@ibexa.api.service.user'] - tags: - - { name: kernel.event_subscriber } +[[= include_file('code_samples/user_management/in_memory/config/services.yaml') =]] ``` Don't mix `MVCEvents::INTERACTIVE_LOGIN` event (specific to [[= product_name =]]) and `SecurityEvents::INTERACTIVE_LOGIN` event (fired by Symfony security component). ``` php - 'onInteractiveLogin' - ]; - } - - public function onInteractiveLogin(InteractiveLoginEvent $event) - { - $userMap = [ - 'from_memory_user' => 'generic_customer_account', - ]; - $userLogin = $userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; - $ibexaUser = $this->userService->loadUserByLogin($userLogin); - $event->getAuthenticationToken()->setUser(new User($ibexaUser)); - - return $event; - } -} +[[= include_file('code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php') =]] ``` From 8e2f61961b313be7dc3803ee33c308c1572648b4 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:35:35 +0100 Subject: [PATCH 04/35] user_authentication.md: start to update explanations --- docs/users/user_authentication.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index d3f63d0687..0fa79f9cf4 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -6,25 +6,26 @@ description: Customize user authentication. ## Authenticate user with multiple user providers -Symfony provides native support for [multiple user providers]([[= symfony_doc =]]/security/user_providers.html). +Symfony provides native support for [multiple user providers]([[= symfony_doc =]]/security/user_providers.html). This makes it easier to integrate any kind of login handlers, including SSO and existing third party bundles (for example, [FR3DLdapBundle](https://github.com/Maks3w/FR3DLdapBundle), [HWIOauthBundle](https://github.com/hwi/HWIOAuthBundle), [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle), or [BeSimpleSsoAuthBundle](https://github.com/BeSimple/BeSimpleSsoAuthBundle)). -However, to be able to use *external* user providers with [[= product_name =]], a valid Platform user needs to be injected into the repository. +However, to be able to use *external* user providers with [[= product_name =]], a valid Ibexa user needs to be injected into the repository. This is mainly for the kernel to be able to manage content-related permissions (but not limited to this). -Depending on your context, you either want to create a Platform user, return an existing user, or even always use a generic user. +Depending on your context, you either want to create and return an Ibexa user, or return an existing user, even a generic one. -Whenever an *external* user is matched (i.e. one that doesn't come from Platform repository, like coming from LDAP), [[= product_name =]] kernel initiates an `MVCEvents::INTERACTIVE_LOGIN` event. -Every service listening to this event receives an `Ibexa\Core\MVC\Symfony\Event\InteractiveLoginEvent` object which contains the original security token (that holds the matched user) and the request. +Whenever an *external* user is matched (i.e. one that doesn't come from Platform repository, like coming from LDAP), [[= product_name =]] kernel initiates a `SecurityEvents::INTERACTIVE_LOGIN` event. +Every service listening to this event receives an `Symfony\Component\Security\Http\Event\InteractiveLoginEvent` object which contains the original security token (that holds the matched user) and the request. -Then, it's up to the listener to retrieve a Platform user from the repository and to assign it back to the event object. +Then, it's up to the listener to retrieve an Ibexa user from the repository and to assign it back to the event object. This user is injected into the repository and used for the rest of the request. -If no [[= product_name =]] user is returned, the Anonymous user is used. +If no [[= product_name =]] user is returned, the Anonymous user is used. TODO: check this statement. ### User exposed and security token -When an *external* user is matched, a different token is injected into the security context, the `InteractiveLoginToken`. +When an *external* user is matched, a different token is injected into the security context, the `InteractiveLoginToken`. +TODO: There is no such token class This token holds a `UserWrapped` instance which contains the originally matched user and the *API user* (the one from the [[= product_name =]] repository). The *API user* is mainly used for permission checks against the repository and thus stays *under the hood*. @@ -32,6 +33,7 @@ The *API user* is mainly used for permission checks against the repository and ### Customize the user class It's possible to customize the user class used by extending `Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener` service, which defaults to `Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener`. +TODO: There is no such class You can override `getUser()` to return whatever user class you want, as long as it implements `Ibexa\Core\MVC\Symfony\Security\UserInterface`. @@ -42,7 +44,7 @@ The following is an example of using the in-memory user provider: [[= include_file('code_samples/user_management/in_memory/config/packages/security.yaml') =]] ``` -### Implement the listener +### Implement the subscriber In the `config/services.yaml` file: @@ -50,8 +52,6 @@ In the `config/services.yaml` file: [[= include_file('code_samples/user_management/in_memory/config/services.yaml') =]] ``` -Don't mix `MVCEvents::INTERACTIVE_LOGIN` event (specific to [[= product_name =]]) and `SecurityEvents::INTERACTIVE_LOGIN` event (fired by Symfony security component). - ``` php [[= include_file('code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php') =]] ``` From 9f61b095f77acc005038b0aac064e72ec678a66e Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:40:15 +0100 Subject: [PATCH 05/35] InteractiveLoginSubscriber::onInteractiveLogin() return type --- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index b5128ead24..c607a7f50c 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -21,7 +21,7 @@ public static function getSubscribedEvents() ]; } - public function onInteractiveLogin(InteractiveLoginEvent $event) + public function onInteractiveLogin(InteractiveLoginEvent $event): InteractiveLoginEvent { $userMap = [ 'from_memory_user' => 'generic_customer_account', From 0e6a2a671d2decb38d0cab31ca8889e8b36bae07 Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Fri, 13 Mar 2026 08:47:36 +0000 Subject: [PATCH 06/35] PHP & JS CS Fixes --- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index c607a7f50c..ea894fba0c 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -4,8 +4,8 @@ use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Core\MVC\Symfony\Security\User; -use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; class InteractiveLoginSubscriber implements EventSubscriberInterface @@ -17,7 +17,7 @@ public function __construct(private readonly UserService $userService) public static function getSubscribedEvents() { return [ - SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin' + SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin', ]; } From a1cb9a9ec36e1d34488ce74ca87e8d4c26789b42 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:11:57 +0100 Subject: [PATCH 07/35] user_authentication.md: Rewrite example description --- .../in_memory/config/packages/security.yaml | 10 ++--- .../in_memory/config/services.yaml | 10 ++--- .../InteractiveLoginSubscriber.php | 18 +++++--- docs/users/user_authentication.md | 44 ++++++++----------- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/code_samples/user_management/in_memory/config/packages/security.yaml b/code_samples/user_management/in_memory/config/packages/security.yaml index 42fafb542b..a5239f50f1 100644 --- a/code_samples/user_management/in_memory/config/packages/security.yaml +++ b/code_samples/user_management/in_memory/config/packages/security.yaml @@ -6,15 +6,15 @@ security: # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded providers: - ibexa: - id: ibexa.security.user_provider in_memory: memory: users: - # You will then be able to log in with username "from_memory_user" and password "from_memory_pass" from_memory_user: { password: from_memory_pass, roles: [ 'ROLE_USER' ] } + from_memory_admin: { password: from_memory_publish, roles: [ 'ROLE_USER' ] } + ibexa: + id: ibexa.security.user_provider # Chaining in_memory and ibexa user providers - chain_provider: + chained: chain: providers: [ in_memory, ibexa ] @@ -22,7 +22,7 @@ security: # … ibexa_front: pattern: ^/ - provider: chain_provider + provider: chained user_checker: Ibexa\Core\MVC\Symfony\Security\UserChecker context: ibexa form_login: diff --git a/code_samples/user_management/in_memory/config/services.yaml b/code_samples/user_management/in_memory/config/services.yaml index 5deab6f8cb..921ff2149c 100644 --- a/code_samples/user_management/in_memory/config/services.yaml +++ b/code_samples/user_management/in_memory/config/services.yaml @@ -1,5 +1,5 @@ -services: - App\EventSubscriber\InteractiveLoginSubscriber: - arguments: ['@ibexa.api.service.user'] - tags: - - { name: kernel.event_subscriber } +App\EventSubscriber\InteractiveLoginSubscriber: + arguments: + $userMap: + from_memory_user: customer + from_memory_admin: admin diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index ea894fba0c..cb8ebfb1af 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -5,12 +5,16 @@ use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Core\MVC\Symfony\Security\User; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; class InteractiveLoginSubscriber implements EventSubscriberInterface { - public function __construct(private readonly UserService $userService) + public function __construct( + private readonly UserService $userService, + private readonly array $userMap = [], + ) { } @@ -23,12 +27,12 @@ public static function getSubscribedEvents() public function onInteractiveLogin(InteractiveLoginEvent $event): InteractiveLoginEvent { - $userMap = [ - 'from_memory_user' => 'generic_customer_account', - ]; - $userLogin = $userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; - $ibexaUser = $this->userService->loadUserByLogin($userLogin); - $event->getAuthenticationToken()->setUser(new User($ibexaUser)); + $tokenUser = $event->getAuthenticationToken()->getUser(); + if ($tokenUser instanceof InMemoryUser) { + $userLogin = $this->userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; + $ibexaUser = $this->userService->loadUserByLogin($userLogin); + $event->getAuthenticationToken()->setUser(new User($ibexaUser)); + } return $event; } diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index 0fa79f9cf4..37c705f5d3 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -14,44 +14,38 @@ This is mainly for the kernel to be able to manage content-related permissions ( Depending on your context, you either want to create and return an Ibexa user, or return an existing user, even a generic one. -Whenever an *external* user is matched (i.e. one that doesn't come from Platform repository, like coming from LDAP), [[= product_name =]] kernel initiates a `SecurityEvents::INTERACTIVE_LOGIN` event. -Every service listening to this event receives an `Symfony\Component\Security\Http\Event\InteractiveLoginEvent` object which contains the original security token (that holds the matched user) and the request. +Whenever a user is matched, Symfony initiates a `SecurityEvents::INTERACTIVE_LOGIN` event. +Every service listening to this event receives an `InteractiveLoginEvent` object which contains the original security token (that holds the matched user) and the request. -Then, it's up to the listener to retrieve an Ibexa user from the repository and to assign it back to the event object. +Then, it's up to a listener to retrieve an Ibexa user from the repository and to assign it back to the event object. This user is injected into the repository and used for the rest of the request. -If no [[= product_name =]] user is returned, the Anonymous user is used. TODO: check this statement. +### User mapping example -### User exposed and security token +The following example uses the [memory user provider]([[= symfony_doc =]]/security/user_providers.html#memory-user-provider), +maps memory user to Ibexa repository user, +and [chains]([[= symfony_doc =]]/security/user_providers.html#chain-user-provider) with the Ibexa user provider to be able to use both: -When an *external* user is matched, a different token is injected into the security context, the `InteractiveLoginToken`. -TODO: There is no such token class -This token holds a `UserWrapped` instance which contains the originally matched user and the *API user* (the one from the [[= product_name =]] repository). +Create as `src/EventSubscriber/InteractiveLoginSubscriber.php` a subscriber listening to the `SecurityEvents::INTERACTIVE_LOGIN` event +and mapping when needed an in-memory authenticated user to an Ibexa user: -The *API user* is mainly used for permission checks against the repository and thus stays *under the hood*. - -### Customize the user class - -It's possible to customize the user class used by extending `Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener` service, which defaults to `Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener`. -TODO: There is no such class - -You can override `getUser()` to return whatever user class you want, as long as it implements `Ibexa\Core\MVC\Symfony\Security\UserInterface`. +``` php +[[= include_file('code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php') =]] +``` -The following is an example of using the in-memory user provider: +In `config/packages/security.yaml`, +add the `memory` and `chain` user providers, +store some in-memory users with their passwords in plain text and a basic role, +set a `plaintext` password encoder for the `memory` provider's `InMemoryUser`, +and configure the firewall to use the `chain` provider: ``` yaml -# config/packages/security.yaml [[= include_file('code_samples/user_management/in_memory/config/packages/security.yaml') =]] ``` -### Implement the subscriber - -In the `config/services.yaml` file: +In the `config/services.yaml` file, declare the subscriber as a service to pass your user map +(it's automatically tagged `kernel.event_subscriber` as implementing the `EventSubscriberInterface`, the user service injection is auto-wired): ``` yaml [[= include_file('code_samples/user_management/in_memory/config/services.yaml') =]] ``` - -``` php -[[= include_file('code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php') =]] -``` From 8b944e2f1922cf4b62ae6f54fd824954ea0f79b3 Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Fri, 13 Mar 2026 11:20:02 +0000 Subject: [PATCH 08/35] PHP & JS CS Fixes --- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index cb8ebfb1af..14c5aa5980 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -14,8 +14,7 @@ class InteractiveLoginSubscriber implements EventSubscriberInterface public function __construct( private readonly UserService $userService, private readonly array $userMap = [], - ) - { + ) { } public static function getSubscribedEvents() From 0e5516ef5cae2765a766e824aa37bc579b060ccb Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:22:35 +0100 Subject: [PATCH 09/35] InteractiveLoginSubscriber: Fix missingType.iterableValue Method App\EventSubscriber\InteractiveLoginSubscriber::__construct() has parameter $userMap with no value type specified in iterable type array. --- .../in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php | 1 + 1 file changed, 1 insertion(+) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index 14c5aa5980..0895ee1a79 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -11,6 +11,7 @@ class InteractiveLoginSubscriber implements EventSubscriberInterface { + /** @param array $userMap */ public function __construct( private readonly UserService $userService, private readonly array $userMap = [], From aab4c05b951c02598a656d90945d42271b9b5cce Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:56:11 +0100 Subject: [PATCH 10/35] InteractiveLoginSubscriber: No need to return the event --- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index 0895ee1a79..ac000c0439 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -25,7 +25,7 @@ public static function getSubscribedEvents() ]; } - public function onInteractiveLogin(InteractiveLoginEvent $event): InteractiveLoginEvent + public function onInteractiveLogin(InteractiveLoginEvent $event): void { $tokenUser = $event->getAuthenticationToken()->getUser(); if ($tokenUser instanceof InMemoryUser) { @@ -33,7 +33,5 @@ public function onInteractiveLogin(InteractiveLoginEvent $event): InteractiveLog $ibexaUser = $this->userService->loadUserByLogin($userLogin); $event->getAuthenticationToken()->setUser(new User($ibexaUser)); } - - return $event; } } From d846a46c8f594df02dc9ebe2028f9747b3d2a835 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 18:45:27 +0100 Subject: [PATCH 11/35] services.yaml: Format --- .../user_management/in_memory/config/services.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/code_samples/user_management/in_memory/config/services.yaml b/code_samples/user_management/in_memory/config/services.yaml index 921ff2149c..83124bea6a 100644 --- a/code_samples/user_management/in_memory/config/services.yaml +++ b/code_samples/user_management/in_memory/config/services.yaml @@ -1,5 +1,6 @@ -App\EventSubscriber\InteractiveLoginSubscriber: - arguments: - $userMap: - from_memory_user: customer - from_memory_admin: admin +services: + App\EventSubscriber\InteractiveLoginSubscriber: + arguments: + $userMap: + from_memory_user: customer + from_memory_admin: admin From c6ef3c5c8233b0fbcec94196bfa5fe864b815db4 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:00:11 +0100 Subject: [PATCH 12/35] user_authentication.md: closer to reality --- docs/users/user_authentication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index 37c705f5d3..a15af3fcca 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -17,8 +17,8 @@ Depending on your context, you either want to create and return an Ibexa user, o Whenever a user is matched, Symfony initiates a `SecurityEvents::INTERACTIVE_LOGIN` event. Every service listening to this event receives an `InteractiveLoginEvent` object which contains the original security token (that holds the matched user) and the request. -Then, it's up to a listener to retrieve an Ibexa user from the repository and to assign it back to the event object. -This user is injected into the repository and used for the rest of the request. +Then, it's up to a listener to retrieve an Ibexa user from the repository. +This user is wrapped within `Ibexa\Core\MVC\Symfony\Security\User` and assigned back into the event's token for the rest of the request. ### User mapping example From 9db8d053dd13611d30141114934a5d7fad9f3a4a Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:35:37 +0100 Subject: [PATCH 13/35] user_authentication.md: wording --- docs/users/user_authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index a15af3fcca..a649b269bb 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -26,7 +26,7 @@ The following example uses the [memory user provider]([[= symfony_doc =]]/securi maps memory user to Ibexa repository user, and [chains]([[= symfony_doc =]]/security/user_providers.html#chain-user-provider) with the Ibexa user provider to be able to use both: -Create as `src/EventSubscriber/InteractiveLoginSubscriber.php` a subscriber listening to the `SecurityEvents::INTERACTIVE_LOGIN` event +Create as `src/EventSubscriber/InteractiveLoginSubscriber.php` subscribing to the `SecurityEvents::INTERACTIVE_LOGIN` event and mapping when needed an in-memory authenticated user to an Ibexa user: ``` php From daec0d1955fe4b87f0ed0bccfe10b5f4a8bb4152 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:56:19 +0100 Subject: [PATCH 14/35] deptrac.baseline.yaml: Ignore InteractiveLoginSubscriber Security\User --- deptrac.baseline.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deptrac.baseline.yaml b/deptrac.baseline.yaml index c1f699e7fa..260eb476fa 100644 --- a/deptrac.baseline.yaml +++ b/deptrac.baseline.yaml @@ -102,6 +102,8 @@ deptrac: - Ibexa\FormBuilder\Event\FormEvents App\EventSubscriber\HelpMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent + App\EventSubscriber\InteractiveLoginSubscriber: + - Ibexa\Core\MVC\Symfony\Security\User App\EventSubscriber\MyMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent - Ibexa\AdminUi\Menu\MainMenuBuilder From 4a372501bc2f6c0cfbdf1e22c1cf71309fcd931e Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:00:39 +0100 Subject: [PATCH 15/35] InteractiveLoginSubscriber: typehint --- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index ac000c0439..82776991c6 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -18,7 +18,7 @@ public function __construct( ) { } - public static function getSubscribedEvents() + public static function getSubscribedEvents(): array { return [ SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin', From fccf5cb3e0a181bf791e1bad374cf9240e829989 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 11:19:31 +0200 Subject: [PATCH 16/35] Apply suggestion from @konradoboza Co-authored-by: Konrad Oboza --- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index 82776991c6..8763c9e698 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -9,7 +9,7 @@ use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; -class InteractiveLoginSubscriber implements EventSubscriberInterface +final readonly class InteractiveLoginSubscriber implements EventSubscriberInterface { /** @param array $userMap */ public function __construct( From 924405fc2a8d009100fc6da8350ba0fe91b971a0 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 11:27:17 +0200 Subject: [PATCH 17/35] readability around instanceof test --- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index 8763c9e698..164880d2cd 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -28,10 +28,11 @@ public static function getSubscribedEvents(): array public function onInteractiveLogin(InteractiveLoginEvent $event): void { $tokenUser = $event->getAuthenticationToken()->getUser(); - if ($tokenUser instanceof InMemoryUser) { - $userLogin = $this->userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; - $ibexaUser = $this->userService->loadUserByLogin($userLogin); - $event->getAuthenticationToken()->setUser(new User($ibexaUser)); + if (!$tokenUser instanceof InMemoryUser) { + return; } + $userLogin = $this->userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; + $ibexaUser = $this->userService->loadUserByLogin($userLogin); + $event->getAuthenticationToken()->setUser(new User($ibexaUser)); } } From 88a7d3462d50a6df5fe34181c1d7d4043f7616c6 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:49:03 +0200 Subject: [PATCH 18/35] Use anonymous_user_id --- .../EventSubscriber/InteractiveLoginSubscriber.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index 164880d2cd..cab039387f 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -3,6 +3,7 @@ namespace App\EventSubscriber; use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Ibexa\Core\MVC\Symfony\Security\User; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\User\InMemoryUser; @@ -13,6 +14,7 @@ { /** @param array $userMap */ public function __construct( + private readonly ConfigResolverInterface $configResolver, private readonly UserService $userService, private readonly array $userMap = [], ) { @@ -31,8 +33,15 @@ public function onInteractiveLogin(InteractiveLoginEvent $event): void if (!$tokenUser instanceof InMemoryUser) { return; } - $userLogin = $this->userMap[$event->getAuthenticationToken()->getUserIdentifier()] ?? 'anonymous'; - $ibexaUser = $this->userService->loadUserByLogin($userLogin); + $userIdentifier = $event->getAuthenticationToken()->getUserIdentifier(); + $ibexaUser = null; + if (array_key_exists($userIdentifier, $this->userMap)) { + $ibexaUser = $this->userService->loadUserByLogin($this->userMap[$userIdentifier]); + } + if (null === $ibexaUser) { + $anonymousUserId = (int)$this->configResolver->getParameter('anonymous_user_id'); + $ibexaUser = $this->userService->loadUser($anonymousUserId); + } $event->getAuthenticationToken()->setUser(new User($ibexaUser)); } } From 9b561aa5f05162f26b1d27332e5a0b53d683dc67 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 16:05:45 +0200 Subject: [PATCH 19/35] UserWrapped and more about example users --- .../in_memory/config/packages/security.yaml | 5 +++-- .../in_memory/config/services.yaml | 2 +- .../InteractiveLoginSubscriber.php | 4 ++-- docs/users/user_authentication.md | 17 +++++++++++++++-- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/code_samples/user_management/in_memory/config/packages/security.yaml b/code_samples/user_management/in_memory/config/packages/security.yaml index a5239f50f1..7cbc8beda7 100644 --- a/code_samples/user_management/in_memory/config/packages/security.yaml +++ b/code_samples/user_management/in_memory/config/packages/security.yaml @@ -9,8 +9,9 @@ security: in_memory: memory: users: - from_memory_user: { password: from_memory_pass, roles: [ 'ROLE_USER' ] } - from_memory_admin: { password: from_memory_publish, roles: [ 'ROLE_USER' ] } + from_memory_user: { password: from_memory_pass, roles: [ 'ROLE_USER' ] } # Mapped to `generic_customer` user + from_memory_anonym: { password: from_memory_anonym, roles: [ 'ROLE_USER' ] } # Not mapped so `anonymous` user is loaded + from_memory_admin: { password: from_memory_publish, roles: [ 'ROLE_USER' ] } # Mapped to `admin` user ibexa: id: ibexa.security.user_provider # Chaining in_memory and ibexa user providers diff --git a/code_samples/user_management/in_memory/config/services.yaml b/code_samples/user_management/in_memory/config/services.yaml index 83124bea6a..026797cd49 100644 --- a/code_samples/user_management/in_memory/config/services.yaml +++ b/code_samples/user_management/in_memory/config/services.yaml @@ -2,5 +2,5 @@ services: App\EventSubscriber\InteractiveLoginSubscriber: arguments: $userMap: - from_memory_user: customer + from_memory_user: generic_customer from_memory_admin: admin diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index cab039387f..0b9c059cae 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -4,7 +4,7 @@ use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; -use Ibexa\Core\MVC\Symfony\Security\User; +use Ibexa\Core\MVC\Symfony\Security\UserWrapped; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; @@ -42,6 +42,6 @@ public function onInteractiveLogin(InteractiveLoginEvent $event): void $anonymousUserId = (int)$this->configResolver->getParameter('anonymous_user_id'); $ibexaUser = $this->userService->loadUser($anonymousUserId); } - $event->getAuthenticationToken()->setUser(new User($ibexaUser)); + $event->getAuthenticationToken()->setUser(new UserWrapped($tokenUser, $ibexaUser)); } } diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index a649b269bb..a5e01dc5fa 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -18,7 +18,13 @@ Whenever a user is matched, Symfony initiates a `SecurityEvents::INTERACTIVE_LOG Every service listening to this event receives an `InteractiveLoginEvent` object which contains the original security token (that holds the matched user) and the request. Then, it's up to a listener to retrieve an Ibexa user from the repository. -This user is wrapped within `Ibexa\Core\MVC\Symfony\Security\User` and assigned back into the event's token for the rest of the request. + +This Ibexa user can be + +- embedded into `Ibexa\Core\MVC\Symfony\Security\User` while forgetting about the original user +- wrapped into `Ibexa\Core\MVC\Symfony\Security\UserWrapped` with the original user if needed + +Finally, this user is assigned back into the event's token for the rest of the request. ### User mapping example @@ -39,7 +45,7 @@ store some in-memory users with their passwords in plain text and a basic role, set a `plaintext` password encoder for the `memory` provider's `InMemoryUser`, and configure the firewall to use the `chain` provider: -``` yaml +``` yaml hl_lines="4 9-14 18-20 26" [[= include_file('code_samples/user_management/in_memory/config/packages/security.yaml') =]] ``` @@ -49,3 +55,10 @@ In the `config/services.yaml` file, declare the subscriber as a service to pass ``` yaml [[= include_file('code_samples/user_management/in_memory/config/services.yaml') =]] ``` + +From the back office, create the mapped users. +For the example, a new user with the login `generic_customer` and a random password for the mapping to work, +this account can be in the **Customers** or the **Anonymous users** group. + +You can now log in with a in-memory user. +In the Symfony debug toolbar, you should see the in-memory user as this example uses `UserWrapped`. From a6804bd2f8c187f65f94365edda8897caadc86a2 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 16:28:27 +0200 Subject: [PATCH 20/35] UserWrapped and more about example users --- docs/users/user_authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index a5e01dc5fa..f69d7c02d2 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -50,7 +50,7 @@ and configure the firewall to use the `chain` provider: ``` In the `config/services.yaml` file, declare the subscriber as a service to pass your user map -(it's automatically tagged `kernel.event_subscriber` as implementing the `EventSubscriberInterface`, the user service injection is auto-wired): +(it's automatically tagged `kernel.event_subscriber` as implementing the `EventSubscriberInterface`, the config resolver and user service injections are auto-wired): ``` yaml [[= include_file('code_samples/user_management/in_memory/config/services.yaml') =]] From b420efba41f5e3338647b660ee1cee78def324c3 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 16:31:01 +0200 Subject: [PATCH 21/35] UserWrapped and more about example users --- .../user_management/in_memory/config/packages/security.yaml | 2 +- .../src/EventSubscriber/InteractiveLoginSubscriber.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/code_samples/user_management/in_memory/config/packages/security.yaml b/code_samples/user_management/in_memory/config/packages/security.yaml index 7cbc8beda7..72f27b50a1 100644 --- a/code_samples/user_management/in_memory/config/packages/security.yaml +++ b/code_samples/user_management/in_memory/config/packages/security.yaml @@ -10,7 +10,7 @@ security: memory: users: from_memory_user: { password: from_memory_pass, roles: [ 'ROLE_USER' ] } # Mapped to `generic_customer` user - from_memory_anonym: { password: from_memory_anonym, roles: [ 'ROLE_USER' ] } # Not mapped so `anonymous` user is loaded + from_memory_forgotten: { password: from_memory_anonym, roles: [ 'ROLE_USER' ] } # Not mapped so `anonymous` user is loaded from_memory_admin: { password: from_memory_publish, roles: [ 'ROLE_USER' ] } # Mapped to `admin` user ibexa: id: ibexa.security.user_provider diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php index 0b9c059cae..3ce8af967d 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php @@ -4,6 +4,7 @@ use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +//use Ibexa\Core\MVC\Symfony\Security\User; use Ibexa\Core\MVC\Symfony\Security\UserWrapped; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\User\InMemoryUser; @@ -42,6 +43,7 @@ public function onInteractiveLogin(InteractiveLoginEvent $event): void $anonymousUserId = (int)$this->configResolver->getParameter('anonymous_user_id'); $ibexaUser = $this->userService->loadUser($anonymousUserId); } + //$event->getAuthenticationToken()->setUser(new User($ibexaUser)); $event->getAuthenticationToken()->setUser(new UserWrapped($tokenUser, $ibexaUser)); } } From 929ff6f98b65f7fdf092c855464f384445de6b20 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 17:20:02 +0200 Subject: [PATCH 22/35] =?UTF-8?q?SecurityEvents::INTERACTIVE=5FLOGIN=20?= =?UTF-8?q?=E2=86=92=20AuthenticationTokenCreatedEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in_memory/config/services.yaml | 2 +- ...=> AuthenticationTokenCreatedSubscriber.php} | 17 ++++++++--------- docs/users/user_authentication.md | 8 ++++---- 3 files changed, 13 insertions(+), 14 deletions(-) rename code_samples/user_management/in_memory/src/EventSubscriber/{InteractiveLoginSubscriber.php => AuthenticationTokenCreatedSubscriber.php} (65%) diff --git a/code_samples/user_management/in_memory/config/services.yaml b/code_samples/user_management/in_memory/config/services.yaml index 026797cd49..a95531ace9 100644 --- a/code_samples/user_management/in_memory/config/services.yaml +++ b/code_samples/user_management/in_memory/config/services.yaml @@ -1,5 +1,5 @@ services: - App\EventSubscriber\InteractiveLoginSubscriber: + App\EventSubscriber\AuthenticationTokenCreatedSubscriber: arguments: $userMap: from_memory_user: generic_customer diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php similarity index 65% rename from code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php rename to code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php index 3ce8af967d..20cba2bf4f 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php @@ -8,10 +8,9 @@ use Ibexa\Core\MVC\Symfony\Security\UserWrapped; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\User\InMemoryUser; -use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; -use Symfony\Component\Security\Http\SecurityEvents; +use Symfony\Component\Security\Http\Event\AuthenticationTokenCreatedEvent; -final readonly class InteractiveLoginSubscriber implements EventSubscriberInterface +final readonly class AuthenticationTokenCreatedSubscriber implements EventSubscriberInterface { /** @param array $userMap */ public function __construct( @@ -24,17 +23,17 @@ public function __construct( public static function getSubscribedEvents(): array { return [ - SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin', + AuthenticationTokenCreatedEvent::class => ['onAuthenticationTokenCreated', 10], ]; } - public function onInteractiveLogin(InteractiveLoginEvent $event): void + public function onAuthenticationTokenCreated(AuthenticationTokenCreatedEvent $event): void { - $tokenUser = $event->getAuthenticationToken()->getUser(); + $tokenUser = $event->getAuthenticatedToken()->getUser(); if (!$tokenUser instanceof InMemoryUser) { return; } - $userIdentifier = $event->getAuthenticationToken()->getUserIdentifier(); + $userIdentifier = $event->getAuthenticatedToken()->getUserIdentifier(); $ibexaUser = null; if (array_key_exists($userIdentifier, $this->userMap)) { $ibexaUser = $this->userService->loadUserByLogin($this->userMap[$userIdentifier]); @@ -43,7 +42,7 @@ public function onInteractiveLogin(InteractiveLoginEvent $event): void $anonymousUserId = (int)$this->configResolver->getParameter('anonymous_user_id'); $ibexaUser = $this->userService->loadUser($anonymousUserId); } - //$event->getAuthenticationToken()->setUser(new User($ibexaUser)); - $event->getAuthenticationToken()->setUser(new UserWrapped($tokenUser, $ibexaUser)); + //$event->getAuthenticatedToken()->setUser(new User($ibexaUser)); + $event->getAuthenticatedToken()->setUser(new UserWrapped($tokenUser, $ibexaUser)); } } diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index f69d7c02d2..cf71300954 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -14,8 +14,8 @@ This is mainly for the kernel to be able to manage content-related permissions ( Depending on your context, you either want to create and return an Ibexa user, or return an existing user, even a generic one. -Whenever a user is matched, Symfony initiates a `SecurityEvents::INTERACTIVE_LOGIN` event. -Every service listening to this event receives an `InteractiveLoginEvent` object which contains the original security token (that holds the matched user) and the request. +Whenever a user is matched, Symfony initiates an `AuthenticationTokenCreatedEvent` event during authentication. +Every service listening to this event receives an object which contains the original security token (that holds the matched user) and a [passport]([[= symfony_doc =]]/security/custom_authenticator.html#security-passports). Then, it's up to a listener to retrieve an Ibexa user from the repository. @@ -32,11 +32,11 @@ The following example uses the [memory user provider]([[= symfony_doc =]]/securi maps memory user to Ibexa repository user, and [chains]([[= symfony_doc =]]/security/user_providers.html#chain-user-provider) with the Ibexa user provider to be able to use both: -Create as `src/EventSubscriber/InteractiveLoginSubscriber.php` subscribing to the `SecurityEvents::INTERACTIVE_LOGIN` event +Create as `src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php` subscribing to the `AuthenticationTokenCreatedEvent` event and mapping when needed an in-memory authenticated user to an Ibexa user: ``` php -[[= include_file('code_samples/user_management/in_memory/src/EventSubscriber/InteractiveLoginSubscriber.php') =]] +[[= include_file('code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php') =]] ``` In `config/packages/security.yaml`, From ef889f084b5c7e17a151e02c4d403eb673c41181 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 17:44:43 +0200 Subject: [PATCH 23/35] Fix deptrac --- deptrac.baseline.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deptrac.baseline.yaml b/deptrac.baseline.yaml index e41e6332fe..d8271e8cc8 100644 --- a/deptrac.baseline.yaml +++ b/deptrac.baseline.yaml @@ -130,6 +130,7 @@ deptrac: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent App\EventSubscriber\InteractiveLoginSubscriber: - Ibexa\Core\MVC\Symfony\Security\User + - Ibexa\Core\MVC\Symfony\Security\UserWrapped App\EventSubscriber\MyMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent - Ibexa\AdminUi\Menu\MainMenuBuilder From 02a1999556358bbd72fa75cb6c56d7d4431ee389 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 17:49:48 +0200 Subject: [PATCH 24/35] Fix deptrac --- deptrac.baseline.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/deptrac.baseline.yaml b/deptrac.baseline.yaml index d8271e8cc8..02d2d60585 100644 --- a/deptrac.baseline.yaml +++ b/deptrac.baseline.yaml @@ -129,7 +129,6 @@ deptrac: App\EventSubscriber\HelpMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent App\EventSubscriber\InteractiveLoginSubscriber: - - Ibexa\Core\MVC\Symfony\Security\User - Ibexa\Core\MVC\Symfony\Security\UserWrapped App\EventSubscriber\MyMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent From 203a5d48a45f80a1753a93eed8b25ab7d222f3f2 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 17:50:06 +0200 Subject: [PATCH 25/35] Fix deptrac --- deptrac.baseline.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deptrac.baseline.yaml b/deptrac.baseline.yaml index 02d2d60585..2fbb924b9d 100644 --- a/deptrac.baseline.yaml +++ b/deptrac.baseline.yaml @@ -129,6 +129,7 @@ deptrac: App\EventSubscriber\HelpMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent App\EventSubscriber\InteractiveLoginSubscriber: + #- Ibexa\Core\MVC\Symfony\Security\User - Ibexa\Core\MVC\Symfony\Security\UserWrapped App\EventSubscriber\MyMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent From 152729f5361ea9cf47e52f9e29bfe4adacdef6b2 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 17:51:36 +0200 Subject: [PATCH 26/35] Fix deptrac --- deptrac.baseline.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deptrac.baseline.yaml b/deptrac.baseline.yaml index e41e6332fe..9fef31d4bc 100644 --- a/deptrac.baseline.yaml +++ b/deptrac.baseline.yaml @@ -120,6 +120,8 @@ deptrac: App\EventListener\TextAnchorMenuTabListener: - Ibexa\AdminUi\Menu\ContentEditAnchorMenuBuilder - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent + App\EventSubscriber\AuthenticationTokenCreatedSubscriber: + - Ibexa\Core\MVC\Symfony\Security\UserWrapped App\EventSubscriber\BreadcrumbsMenuSubscriber: - Ibexa\Bundle\Storefront\Menu\Builder\BreadcrumbsMenuBuilder App\EventSubscriber\FormFieldDefinitionSubscriber: @@ -128,8 +130,6 @@ deptrac: - Ibexa\FormBuilder\Event\FormEvents App\EventSubscriber\HelpMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent - App\EventSubscriber\InteractiveLoginSubscriber: - - Ibexa\Core\MVC\Symfony\Security\User App\EventSubscriber\MyMenuSubscriber: - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent - Ibexa\AdminUi\Menu\MainMenuBuilder From d7de84ea542badf105e2c2e4eb69d448bbc4399d Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Mon, 8 Jun 2026 17:55:11 +0200 Subject: [PATCH 27/35] Fix deptrac --- deptrac.baseline.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deptrac.baseline.yaml b/deptrac.baseline.yaml index 9fef31d4bc..14b0c9e538 100644 --- a/deptrac.baseline.yaml +++ b/deptrac.baseline.yaml @@ -121,6 +121,7 @@ deptrac: - Ibexa\AdminUi\Menu\ContentEditAnchorMenuBuilder - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent App\EventSubscriber\AuthenticationTokenCreatedSubscriber: + #- Ibexa\Core\MVC\Symfony\Security\User - Ibexa\Core\MVC\Symfony\Security\UserWrapped App\EventSubscriber\BreadcrumbsMenuSubscriber: - Ibexa\Bundle\Storefront\Menu\Builder\BreadcrumbsMenuBuilder From 2e39549696f0fb90cc5caf06330286b2d80c0bdb Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:27:49 +0200 Subject: [PATCH 28/35] Apply suggestion from @adriendupuis --- docs/users/user_authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index cf71300954..96ac9d92cd 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -14,7 +14,7 @@ This is mainly for the kernel to be able to manage content-related permissions ( Depending on your context, you either want to create and return an Ibexa user, or return an existing user, even a generic one. -Whenever a user is matched, Symfony initiates an `AuthenticationTokenCreatedEvent` event during authentication. +Whenever a user is matched and authenticated, Symfony initiates an `AuthenticationTokenCreatedEvent`. Every service listening to this event receives an object which contains the original security token (that holds the matched user) and a [passport]([[= symfony_doc =]]/security/custom_authenticator.html#security-passports). Then, it's up to a listener to retrieve an Ibexa user from the repository. From f3f7786917ecaeb53b38b19afe5369bdcc95aa56 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:37:40 +0200 Subject: [PATCH 29/35] AuthenticationTokenCreatedSubscriber: facto, clean-up --- .../AuthenticationTokenCreatedSubscriber.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php index 20cba2bf4f..41d5c6a580 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php @@ -4,7 +4,6 @@ use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; -//use Ibexa\Core\MVC\Symfony\Security\User; use Ibexa\Core\MVC\Symfony\Security\UserWrapped; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\User\InMemoryUser; @@ -29,11 +28,12 @@ public static function getSubscribedEvents(): array public function onAuthenticationTokenCreated(AuthenticationTokenCreatedEvent $event): void { - $tokenUser = $event->getAuthenticatedToken()->getUser(); + $token = $event->getAuthenticatedToken(); + $tokenUser = $token->getUser(); if (!$tokenUser instanceof InMemoryUser) { return; } - $userIdentifier = $event->getAuthenticatedToken()->getUserIdentifier(); + $userIdentifier = $token->getUserIdentifier(); $ibexaUser = null; if (array_key_exists($userIdentifier, $this->userMap)) { $ibexaUser = $this->userService->loadUserByLogin($this->userMap[$userIdentifier]); @@ -42,7 +42,6 @@ public function onAuthenticationTokenCreated(AuthenticationTokenCreatedEvent $ev $anonymousUserId = (int)$this->configResolver->getParameter('anonymous_user_id'); $ibexaUser = $this->userService->loadUser($anonymousUserId); } - //$event->getAuthenticatedToken()->setUser(new User($ibexaUser)); - $event->getAuthenticatedToken()->setUser(new UserWrapped($tokenUser, $ibexaUser)); + $token->setUser(new UserWrapped($tokenUser, $ibexaUser)); } } From 32693c6ad2d4cdfc56e12c4eb242cbcdb573e2ee Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:38:44 +0200 Subject: [PATCH 30/35] Apply suggestion from @adriendupuis --- deptrac.baseline.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/deptrac.baseline.yaml b/deptrac.baseline.yaml index 14b0c9e538..9fef31d4bc 100644 --- a/deptrac.baseline.yaml +++ b/deptrac.baseline.yaml @@ -121,7 +121,6 @@ deptrac: - Ibexa\AdminUi\Menu\ContentEditAnchorMenuBuilder - Ibexa\AdminUi\Menu\Event\ConfigureMenuEvent App\EventSubscriber\AuthenticationTokenCreatedSubscriber: - #- Ibexa\Core\MVC\Symfony\Security\User - Ibexa\Core\MVC\Symfony\Security\UserWrapped App\EventSubscriber\BreadcrumbsMenuSubscriber: - Ibexa\Bundle\Storefront\Menu\Builder\BreadcrumbsMenuBuilder From 1d35a779a46eefe744b56e61251d0c2ecdfe0fae Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:25:48 +0200 Subject: [PATCH 31/35] AuthenticationTokenCreatedSubscriber: increase priority --- .../EventSubscriber/AuthenticationTokenCreatedSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php b/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php index 41d5c6a580..4f351028c5 100644 --- a/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php +++ b/code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php @@ -22,7 +22,7 @@ public function __construct( public static function getSubscribedEvents(): array { return [ - AuthenticationTokenCreatedEvent::class => ['onAuthenticationTokenCreated', 10], + AuthenticationTokenCreatedEvent::class => ['onAuthenticationTokenCreated', 11], ]; } From 1f7e81c01970352c4efa1719596afe91c20eddcb Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:38:38 +0200 Subject: [PATCH 32/35] user_authentication.md: about priority --- docs/users/user_authentication.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index 96ac9d92cd..8d24b4aa41 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -24,7 +24,7 @@ This Ibexa user can be - embedded into `Ibexa\Core\MVC\Symfony\Security\User` while forgetting about the original user - wrapped into `Ibexa\Core\MVC\Symfony\Security\UserWrapped` with the original user if needed -Finally, this user is assigned back into the event's token for the rest of the request. +Finally, this user is assigned back into the event's token for the rest of the process. ### User mapping example @@ -56,9 +56,19 @@ In the `config/services.yaml` file, declare the subscriber as a service to pass [[= include_file('code_samples/user_management/in_memory/config/services.yaml') =]] ``` +You can list the subscribers with the following command to check their order: + +``` bash +php bin/console debug:event-dispatcher AuthenticationTokenCreatedEvent +``` + +Notice that the example subscriber priority is `11` so it's executed before +the `Ibexa\Core\MVC\Symfony\Security\Authentication\EventSubscriber\OnAuthenticationTokenCreatedRepositoryUserSubscriber` +which set the Ibexa user as the current user. + From the back office, create the mapped users. For the example, a new user with the login `generic_customer` and a random password for the mapping to work, this account can be in the **Customers** or the **Anonymous users** group. -You can now log in with a in-memory user. +You can now log in with an in-memory user. In the Symfony debug toolbar, you should see the in-memory user as this example uses `UserWrapped`. From feec9ce723e08d3141e420d3fb80088c9f6856fa Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 9 Jun 2026 14:04:37 +0200 Subject: [PATCH 33/35] user_authentication.md month_change: true --- docs/users/user_authentication.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index 8d24b4aa41..3d2982e767 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -1,5 +1,6 @@ --- description: Customize user authentication. +month_change: true --- # User authentication From e05f9eae83966ec126ba160d2acda21a1d9bf336 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 10 Jun 2026 14:41:21 +0200 Subject: [PATCH 34/35] Apply suggestions from code review Co-authored-by: julitafalcondusza <117284672+julitafalcondusza@users.noreply.github.com> --- docs/users/user_authentication.md | 34 ++++++++++++------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index 3d2982e767..6df26c8bff 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -10,48 +10,42 @@ month_change: true Symfony provides native support for [multiple user providers]([[= symfony_doc =]]/security/user_providers.html). This makes it easier to integrate any kind of login handlers, including SSO and existing third party bundles (for example, [FR3DLdapBundle](https://github.com/Maks3w/FR3DLdapBundle), [HWIOauthBundle](https://github.com/hwi/HWIOAuthBundle), [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle), or [BeSimpleSsoAuthBundle](https://github.com/BeSimple/BeSimpleSsoAuthBundle)). -However, to be able to use *external* user providers with [[= product_name =]], a valid Ibexa user needs to be injected into the repository. +However, to be able to use *external* user providers with [[= product_name =]], a valid [[= product_name_base =]] user needs to be injected into the repository. This is mainly for the kernel to be able to manage content-related permissions (but not limited to this). -Depending on your context, you either want to create and return an Ibexa user, or return an existing user, even a generic one. +Depending on your context, you either want to create and return an [[= product_name_base =]] user, or return an existing user, even a generic one. Whenever a user is matched and authenticated, Symfony initiates an `AuthenticationTokenCreatedEvent`. -Every service listening to this event receives an object which contains the original security token (that holds the matched user) and a [passport]([[= symfony_doc =]]/security/custom_authenticator.html#security-passports). +Every service listening to this event receives an object containing the original security token, which holds the matched user, and a [passport]([[= symfony_doc =]]/security/custom_authenticator.html#security-passports). -Then, it's up to a listener to retrieve an Ibexa user from the repository. +Then, it's up to a listener to retrieve an [[= product_name_base =]] user from the repository. -This Ibexa user can be +This [[= product_name_base =]] user can be: - embedded into `Ibexa\Core\MVC\Symfony\Security\User` while forgetting about the original user - wrapped into `Ibexa\Core\MVC\Symfony\Security\UserWrapped` with the original user if needed -Finally, this user is assigned back into the event's token for the rest of the process. +Finally, the user is assigned back into the event's token for the rest of the process. ### User mapping example -The following example uses the [memory user provider]([[= symfony_doc =]]/security/user_providers.html#memory-user-provider), -maps memory user to Ibexa repository user, -and [chains]([[= symfony_doc =]]/security/user_providers.html#chain-user-provider) with the Ibexa user provider to be able to use both: +The following example uses the [memory user provider]([[= symfony_doc =]]/security/user_providers.html#memory-user-provider), maps memory user to [[= product_name_base =]] repository user, and [chains]([[= symfony_doc =]]/security/user_providers.html#chain-user-provider) with the [[= product_name_base =]] user provider to be able to use both. -Create as `src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php` subscribing to the `AuthenticationTokenCreatedEvent` event -and mapping when needed an in-memory authenticated user to an Ibexa user: +Create a `src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php` that subscribes to the `AuthenticationTokenCreatedEvent` event and maps an authenticated in-memory user to an [[= product_name_base =]] user when necessary: ``` php [[= include_file('code_samples/user_management/in_memory/src/EventSubscriber/AuthenticationTokenCreatedSubscriber.php') =]] ``` -In `config/packages/security.yaml`, -add the `memory` and `chain` user providers, -store some in-memory users with their passwords in plain text and a basic role, -set a `plaintext` password encoder for the `memory` provider's `InMemoryUser`, -and configure the firewall to use the `chain` provider: +In `config/packages/security.yaml`, add the `memory` and `chain` user providers, store some in-memory users with their passwords in plain text and a basic role, set a `plaintext` password encoder for the `memory` provider's `InMemoryUser`, and configure the firewall to use the `chain` provider: ``` yaml hl_lines="4 9-14 18-20 26" [[= include_file('code_samples/user_management/in_memory/config/packages/security.yaml') =]] ``` -In the `config/services.yaml` file, declare the subscriber as a service to pass your user map -(it's automatically tagged `kernel.event_subscriber` as implementing the `EventSubscriberInterface`, the config resolver and user service injections are auto-wired): +In the `config/services.yaml` file, declare the subscriber as a service to pass your user map. +Since it implements the `EventSubscriberInterface`, it's automatically tagged as a `kernel.event_subscriber`. +The config resolver and user service injections are auto-wired automatically. ``` yaml [[= include_file('code_samples/user_management/in_memory/config/services.yaml') =]] @@ -63,9 +57,7 @@ You can list the subscribers with the following command to check their order: php bin/console debug:event-dispatcher AuthenticationTokenCreatedEvent ``` -Notice that the example subscriber priority is `11` so it's executed before -the `Ibexa\Core\MVC\Symfony\Security\Authentication\EventSubscriber\OnAuthenticationTokenCreatedRepositoryUserSubscriber` -which set the Ibexa user as the current user. +Notice that the example subscriber priority is `11` so it's executed before the `Ibexa\Core\MVC\Symfony\Security\Authentication\EventSubscriber\OnAuthenticationTokenCreatedRepositoryUserSubscriber` which set the [[= product_name_base =]] user as the current user. From the back office, create the mapped users. For the example, a new user with the login `generic_customer` and a random password for the mapping to work, From e3ca1de120ed9e659f431d031a2d56bdef341c24 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:05:12 +0200 Subject: [PATCH 35/35] Update docs/users/user_authentication.md --- docs/users/user_authentication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/users/user_authentication.md b/docs/users/user_authentication.md index 6df26c8bff..9c3f23c104 100644 --- a/docs/users/user_authentication.md +++ b/docs/users/user_authentication.md @@ -60,8 +60,8 @@ php bin/console debug:event-dispatcher AuthenticationTokenCreatedEvent Notice that the example subscriber priority is `11` so it's executed before the `Ibexa\Core\MVC\Symfony\Security\Authentication\EventSubscriber\OnAuthenticationTokenCreatedRepositoryUserSubscriber` which set the [[= product_name_base =]] user as the current user. From the back office, create the mapped users. -For the example, a new user with the login `generic_customer` and a random password for the mapping to work, -this account can be in the **Customers** or the **Anonymous users** group. +For this example, create a new user with the login `generic_customer` and a random password so the mapping works correctly. +This account can belong to either the **Customers** or the **Anonymous users** group. You can now log in with an in-memory user. In the Symfony debug toolbar, you should see the in-memory user as this example uses `UserWrapped`.