Mapping between security.yaml providers and doctrine.yaml connections

In my Symfony project, I configure the security of my application in the security.yaml file, where I define different providers for authentication. In parallel, I have also configured multiple database connections in the doctrine.yaml file to access different databases. My goal is that when a user logs in, they can choose a domain that gives them access to one of three different databases, depending on their choice. Although the databases have the same structure, they do not contain the same data, hence the configuration you can see in the attached files. This configuration is not a choice but a necessity.

To manage the connection to these three databases, I rely on three different routes. Depending on the selected domain, a different route is used. These routes (defined in routes.yaml) are linked to my firewalls in security.yaml, and I link my firewalls to providers to finally associate them with my connections in doctrine.yaml, using the “manager_name” option in the providers. Normally, this option should correspond to the name of my connection in doctrine.yaml.

My routes.yaml :

controllers:
  resource: ../src/Controller/
  type: attribute

app:
  path: /
  controller: AppControllerDefaultController::index
  requirements:
    _method: GET
  options:
    index: true

fallback:
  path: /{url}
  controller: AppControllerDefaultController::index
  requirements:
    url: ".+"
  options:
    index: true

delete_livrable:
  path: /api/deleteLivrable/{id}
  controller: AppControllerLivrableController::deleteLivrable
  methods: DELETE

api_login_check:
  path: /api/login_check

api_login_check_data_processing:
    path: /api/login_check_data_processing

api_login_check_data_intelligence:
    path: /api/login_check_data_intelligence```

My doctrine.yaml :

`
doctrine:
  dbal:
    default_connection: data_metier
    connections:
        data_metier:
            dbname: matrics
            host: localhost
            port: 3306
            url: '%env(resolve:DATABASE_URL)%'
            driver: "pdo_mysql"
            driver_class: AppDBALMyDatabaseDriver
            charset: utf8mb4
            server_version: '15'

        data_processing:
            dbname: 
            host: localhost
            port: 3306
            url: '%env(resolve:DATABASE_DATA_PROCESSING_URL)%'
            driver: "pdo_mysql"
            driver_class: AppDBALMyDatabaseDriver
            charset: utf8mb4
            server_version: '15'

        data_intelligence:
            dbname: matricsdataintelligence
            host: localhost
            port: 3306
            url: '%env(resolve:DATABASE_DATA_INTELLIGENCE_URL)%'
            driver: "pdo_mysql"
            driver_class: AppDBALMyDatabaseDriver
            charset: utf8mb4
            server_version: '15'
  orm:
    default_entity_manager: data_processing
    entity_managers:
        data_metier:
          connection: data_metier
          naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
          mappings:
              App:
                  is_bundle: false
                  dir: '%kernel.project_dir%/src/Entity'
                  prefix: 'AppEntity'
                  alias: App

        data_processing:
          connection: data_processing
          naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
          mappings:
              App:
                  is_bundle: false
                  dir: '%kernel.project_dir%/src/Entity'
                  prefix: 'AppEntity'
                  alias: App

        data_intelligence:
          connection: data_intelligence
          naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
          mappings:
              App:
                  is_bundle: false
                  dir: '%kernel.project_dir%/src/Entity'
                  prefix: 'AppEntity'
                  alias: App

when@test:
  doctrine:
    dbal:
      # "TEST_TOKEN" is typically set by ParaTest
      dbname_suffix: "_test%env(default::TEST_TOKEN)%"

when@prod:
  doctrine:
    orm:
      auto_generate_proxy_classes: false
      proxy_dir: "%kernel.build_dir%/doctrine/orm/Proxies"
      entity_managers:
        data_metier:
          query_cache_driver:
            type: pool
            pool: doctrine.system_cache_pool
          result_cache_driver:
            type: pool
            pool: doctrine.result_cache_pool

        data_processing:
          query_cache_driver:
            type: pool
            pool: doctrine.system_cache_pool
          result_cache_driver:
            type: pool
            pool: doctrine.result_cache_pool

        data_intelligence:
          query_cache_driver:
            type: pool
            pool: doctrine.system_cache_pool
          result_cache_driver:
            type: pool
            pool: doctrine.result_cache_pool

  framework:
    cache:
      pools:
        doctrine.result_cache_pool:
          adapter: cache.app
        doctrine.system_cache_pool:
          adapter: cache.system

`
My security.yaml :

`security:
  enable_authenticator_manager: true
  password_hashers:
    SymfonyComponentSecurityCoreUserPasswordAuthenticatedUserInterface: 'auto'
    AppEntityUser:
      algorithm: auto

  providers:
    data_metier_provider: # Le provider pour "S.I Processing"
      entity:
        class: AppEntityUser
        property: user
        manager_name: data_metier

    data_processing_provider:  # Le provider pour "Data Processing"
      entity:
        class: AppEntityUser
        property: user
        manager_name: data_processing

    data_intelligence_provider:  # Le provider pour "Data Intelligence"
      entity:
        class: AppEntityUser
        property: user
        manager_name: data_intelligence
    
    app_users:
        chain:
            providers: ['data_metier_provider', 'data_processing_provider', 'data_intelligence_provider']
      
  firewalls:
    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js)/
      security: false
    
    login_data_processing:
      pattern: ^/api/login_check_data_processing
      logout: ~
      stateless: true
      json_login:
        provider: data_processing_provider
        check_path: /api/login_check_data_processing
        username_path: user_formLogin
        password_path: password_formLogin
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure
      switch_user:
        provider: data_processing_provider
      context: doctrine.security.switch_user.data_processing.target_path

    login_data_intelligence:
      pattern: ^/api/login_check_data_intelligence
      logout: ~
      stateless: true
      json_login:
        provider: data_intelligence_provider
        check_path: /api/login_check_data_intelligence
        username_path: user_formLogin
        password_path: password_formLogin
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure
      switch_user:
        provider: data_intelligence_provider
      context: doctrine.security.switch_user.data_intelligence.target_path
    
    login:
      pattern: ^/api/login_check
      stateless: true
      logout: ~
      json_login:
        provider: data_metier_provider
        check_path: /api/login_check
        username_path: user_formLogin
        password_path: password_formLogin
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure
      switch_user:
        provider: data_metier_provider
      context: doctrine.security.switch_user.data_metier.target_path
    
    api:
      pattern: ^/api
      stateless: true
      jwt:
        provider: app_users

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used

  access_control:
    - { path: ^/api/login_check_data_processing, roles: PUBLIC_ACCESS }
    - { path: ^/api/login_check_data_intelligence, roles: PUBLIC_ACCESS }
    - { path: ^/api/login_check, roles: PUBLIC_ACCESS }
    - { path: ^/api/login/connect, roles: IS_AUTHENTICATED_FULLY }
    - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

when@test:
  security:
    password_hashers:
    SymfonyComponentSecurityCoreUserPasswordAuthenticatedUserInterface:
      algorithm: auto
      cost: 4 # Lowest possible value for bcrypt
      time_cost: 3 # Lowest possible value for argon
      memory_cost: 10 # Lowest possible value for argon`

Connections to the different databases work correctly, and my URLs are defined in the .env file. However, the problem lies in the mapping between my providers and my connections. No matter which provider is used (based on the route and firewall), the mapping with the connections in doctrine.yaml always selects the first connection defined in the configuration (for example, the data_metier connection). If I reverse the order of my connections, it selects the one that comes first. Thus, I can manually change databases via the code. But regardless of the choice made by the user, they are always connected to the same database.

So, I would like to know if my configuration contains an error. Is there a best practice for mapping a security provider to a database connection in Symfony? Is it possible to explicitly configure which provider should use which connection? I am also open to other approaches than using the “manager_name” option, as it seems to make no difference at all.

Overall, I’ve attempted to resolve the issues by modifying variable names in cases where a particular character might have caused a problem. I’ve also explored the option of using a single route and thus a single firewall, which I would have linked to the three providers using the “app_users” chain. Although my expectations were low, I still tried this approach. Additionally, I’ve tried to specify as many options as possible in my configurations, in case something was overlooked. Despite all these attempts, I find myself at a standstill and not sure which direction to take next.

I have been facing this issue for several days without finding a solution. I appreciate your help in advance.