symfony fosuserbundle - Как переопределить логику логина?

Не могу понять, где можно изменить логику идентификации пользователя.

Дано:

Использую FOSUserBundle Symfony.
Были переопределена форма логина. По умолчанию, идентификация происходит по username и password (либо по email - можно переопределить). Мне же нужно, чтобы пользователь идентифицировался по полю mobileNumber.

{{ form_start(form, {'method': 'post', 'action': path('fos_user_security_check')}) }}
    {% if csrf_token %}
            <input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />
    {% endif %}
                    
     <div class="main-text">Войти</div>
                    
     <div class="text-description-mobile-phone">Мобильный телефон</div>
    {{ form_widget(form.mobileNumber) }}
                    
    <div class="text-description-password">Код из СМС</div>
    {{ form_widget(form.code) }}
                    
    <div class="line"></div>
                    
    <input type="submit" id="_submit" name="_submit" value="{{ 'Войти'|trans }}" class="enter-button"/>
                
{{ form_end(form) }}

Что уже достигнуто:

Можно было бы переопределить SecurityController (т.е. скопировать его код к себе в приложение, чтобы оно использовало мой класс, а не из вендора), НО нет смысла, т.к. он не принимает POST и данные формы вообще не обрабатывает.
Не понятно, где конкретно происходит проверка. Вроде бы в самом бандле она реализуется классом LoginManager, но он не переопределяется(или я не знаю, как). Он как-то вызывается внутренней логикой бандла.
При submit-е формы POST отправляется по адресу login_check, но где он находится в бандле не очень понятно, и где его можно переопределить у меня тоже. И как - тоже. Есть предположение, что этот путь можно дать методу CheckAction SecurityController-а, но в бандле этот метод не определён, а аутентификация работает. Также путь 'fos_user_security_check' прописан в action-e формы логина. Это зацепка, но она тонет в путанице роутингов и темноте инкапсуляции.

Решение:

Решение нашлось внезапно на просторах интернета.

  1. Помещаем всю внутреннюю логику залогинивания в loginAction()
  2. Изменяем action обрабатываемой формы на login или
    {{ form_start(form, {'method': 'post', 'action': path('fos_user_security_login')}) }}

Вот код нового контроллера:

   /**
     * @param Request $request
     *
     * @return Response
     * 
     * @Route("login")
     */
   public function loginAction(Request $request): Response
    {
        // Объявляем данные, которые передаются в форму
        $data = [];
        $data['error'] = null;
        
        // Это данные, полученные из формы
        $formData = $request->get('form');
        // Но для отладки можно просто присвоить данные напрямую
//        $mobileNumber = '+71112223456';
        
        // Проверка "если был submit формы"
        if(!empty($request->get('submit'))) {
            
            // Достаём данные из формы
            $mobileNumber = $formData['mobileNumber'];
            $code = $formData['code'];
            
            // Получаем security encoder Symfony
            $factory = $this->get('security.encoder_factory');

            // Загрузка пользователя из базы
            // Если вы используете FOSUserBundle и принимаете username можно как-то так:
    //        $user_manager = $this->get('fos_user.user_manager');
    //        $user = $user_manager->findUserByUsername(['_username' => $username]);
            // Но мы я принимаю mobileNumber, поэтому воспользуюсь Doctrine:
            $user = $this->getDoctrine()->getManager()->getRepository(User::class)
                    ->findOneBy(['mobileNumber' => $mobileNumber]);
            /// Закончили загрузку
            
            // Проверяем, существует ли у нас такой пользователь
            if(!$user){
                // Этот вариант рабочий, отправляет пользователя на сраницу регистрации, если введённого номера нет в базе.
                $url = $this->generateUrl('fos_user_registration_register');
                $response = new RedirectResponse($url);
                return $response;

                // Этот вариант используем для отладки
//                $data['error'] = 'Неверные данные'; 
//                return $this->renderLogin($data);
            }
            
            // Генерируем csrfToken
            $csrfToken = $this->has('security.csrf.token_manager')
                ? $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue()
                : null;
            $data['csrf_token'] = $csrfToken; 
            // Закончили генерировать

            // Верификация пароля (если таковой имеется). У меня регистрация только по мобильному номеру, поэтому закомментировано
//            $encoder = $factory->getEncoder($user);
//            $salt = $user->getSalt();
//
//            if(!$encoder->isPasswordValid($user->getPassword(), $_password, $salt)) {
//                return new Response(
//                    'Username or Password not valid.',
//                    Response::HTTP_UNAUTHORIZED,
//                    array('Content-type' => 'application/json')
//                );
//            } 
            // Закончили верифицировать

            // Пароль подтверждён! Теперь запишем выбранного пользователя в сессию

            // Процесс залогинивания пользователя
            // Третий параметр, "main" может быть другим, в соответствии с вашим firewall-ом в security.yml
            $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
            $this->get('security.token_storage')->setToken($token);

            // Если имя используемого firewall-a не "main", то используйте первую строку:
            // $this->get('session')->set('_security_XXXFIREWALLNAMEXXX', serialize($token));
            $this->get('session')->set('_security_main', serialize($token));

            // Вручную вызываем событие login
            $event = new InteractiveLoginEvent($request, $token);
            $this->get("event_dispatcher")->dispatch("security.interactive_login", $event);

            /*
             * Теперь пользователь аутентифицирован !!!! 
             * Добавьте сюда свой код, если вы хотите сделать что-то ещё, например, печать формы или переадресацию
             */
            return $this->redirectToRoute("meeting");
        }
        
        return $this->renderLogin($data); 
    }

Источник

https://ourcodeworld.com/articles/read/4...

Key Words for FKN + antitotal forum (CS VSU):