{"record":{"id":"3dce5ba93091fa9a","repo":"symfony/symfony","slug":"using-ldap-users-only-on-the-s-firewall-requi","errorCode":null,"errorMessage":"Using \"ldap_users_only\" on the \"%s\" firewall requires a user provider that returns \"%s\" instances, but none of the providers it uses does.","messagePattern":"Using \"ldap_users_only\" on the \"(.+?)\" firewall requires a user provider that returns \"(.+?)\" instances, but none of the providers it uses does\\.","errorType":"validation","errorClass":"InvalidConfigurationException","httpStatus":null,"severity":"error","filePath":"src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LdapFactoryTrait.php","lineNumber":65,"sourceCode":"        // well, so move the decorated one aside and hand the id back, to let both variants be\n        // configured on the same firewall\n        $decoratedId = 'security.authenticator.'.$key.'.'.$firewallName.'.inner';\n        $container->setDefinition($decoratedId, $container->getDefinition($authenticatorId));\n        $container->removeDefinition($authenticatorId);\n\n        if (isset($definitions[$authenticatorId])) {\n            $container->setDefinition($authenticatorId, $definitions[$authenticatorId]);\n        }\n\n        $authenticatorId = $decoratedId;\n\n        if ($config['ldap_users_only']) {\n            if (!property_exists(CheckLdapCredentialsListener::class, 'ldapUsersOnly')) {\n                throw new InvalidConfigurationException('Using \"ldap_users_only\" requires symfony/ldap 8.2 or higher, the installed version would ignore it.');\n            }\n\n            if (false === self::providesLdapUsers($container, $userProviderId)) {\n                throw new InvalidConfigurationException(\\sprintf('Using \"ldap_users_only\" on the \"%s\" firewall requires a user provider that returns \"%s\" instances, but none of the providers it uses does.', $firewallName, LdapUser::class));\n            }\n        }\n\n        $container->setDefinition('security.listener.'.$key.'.'.$firewallName, new Definition(CheckLdapCredentialsListener::class))\n            ->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$firewallName])\n            ->addArgument(new Reference('security.ldap_locator'))\n            ->addArgument($config['ldap_users_only'])\n        ;\n\n        $ldapAuthenticatorId = 'security.authenticator.'.$key.'.'.$firewallName;\n        $definition = $container->setDefinition($ldapAuthenticatorId, new Definition(LdapAuthenticator::class))\n            ->setArguments([\n                new Reference($authenticatorId),\n                $config['service'],\n                $config['dn_string'],\n                $config['search_dn'],\n                $config['search_password'],\n            ]);","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/symfony/symfony/blob/698e28026c22cf35d032cdb6e800db48b1535790/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LdapFactoryTrait.php#L47-L83","documentation":"Thrown when you enable the \"ldap_users_only\" option on an LDAP-authenticating firewall (e.g. form_login_ldap) but the configured user provider(s) do not return Symfony\\Component\\Ldap\\Security\\LdapUser instances. The flag tells CheckLdapCredentialsListener to authenticate only users sourced directly from LDAP, so at container-compile time the bundle calls providesLdapUsers() which recursively inspects the provider (and ChainUserProvider legs) for an LdapUserProvider.","triggerScenarios":"Configuring a firewall with an authenticator whose key ends in \"-ldap\" (form_login_ldap, json_login_ldap, http_basic_ldap) and setting ldap_users_only: true, while the firewall/provider resolves to a non-LDAP provider (memory, entity, or a custom class not extending LdapUserProvider).","commonSituations":"Copying LDAP example config but leaving the default memory/entity provider in place; using a ChainUserProvider where none of the legs is an LdapUserProvider; enabling ldap_users_only after an upgrade without switching the provider to type ldap.","solutions":["Set the firewall's provider key to an LDAP user provider configured under security.providers with type ldap.","If using a chain provider, ensure at least one leg is an LdapUserProvider.","If you also need to authenticate non-LDAP users, remove ldap_users_only: true."],"exampleFix":"# before\nsecurity:\n  firewalls:\n    main:\n      provider: users_in_memory\n      form_login_ldap:\n        service: Symfony\\Component\\Ldap\\Ldap\n        dn_string: 'ou=users,dc=example,dc=com'\n        ldap_users_only: true\n# after\nsecurity:\n  providers:\n    my_ldap:\n      ldap:\n        service: Symfony\\Component\\Ldap\\Ldap\n        base_dn: 'ou=users,dc=example,dc=com'\n        search_dn: 'cn=admin,dc=example,dc=com'\n        search_password: secret\n  firewalls:\n    main:\n      provider: my_ldap\n      form_login_ldap:\n        service: Symfony\\Component\\Ldap\\Ldap\n        dn_string: 'ou=users,dc=example,dc=com'\n        ldap_users_only: true","handlingStrategy":"validation","validationCode":"// Before enabling ldap_users_only, verify the provider chain yields LdapUser\nuse Symfony\\Component\\Ldap\\Security\\LdapUserProvider;\n\n$providerId = $firewall['provider'] ?? null;\n$def = $container->findDefinition($providerId);\n$class = $def?->getClass();\nif ($class && !is_a($class, LdapUserProvider::class, true)) {\n    throw new \\LogicException(\"ldap_users_only requires a LdapUserProvider; {$class} won't work.\");\n}","typeGuard":"function isLdapUserProvider(string $class): bool\n{\n    return is_a($class, \\Symfony\\Component\\Ldap\\Security\\LdapUserProvider::class, true);\n}","tryCatchPattern":null,"preventionTips":["Always pair ldap_users_only with an ldap-type provider under security.providers.","When using a chain provider, confirm at least one leg is LdapUserProvider.","Run the container compile in CI (php bin/console lint:container) to catch this before deploy."],"tags":["security","ldap","configuration","auth","user-provider"],"analyzedSha":"698e28026c22cf35d032cdb6e800db48b1535790","analyzedAt":"2026-08-06T23:40:49.025Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}