opensky/ldap-bundle
Symfony bundle that adds LDAP authentication to your application, letting users sign in against an LDAP directory and map directory entries to local user accounts. Simplifies configuring LDAP connections, user providers, and login flows.
opensky/ldap-bundle targets the Symfony 2.x/early 3.x era and has not kept pace with modern Symfony. Before anything else, run composer require opensky/ldap-bundle in a scratch project and read the solver output — if your symfony/* constraints conflict, stop here and use Symfony's native ldap firewall (form_login_ldap) or fr3d/ldap-bundle instead.app/AppKernel.php (or config/bundles.php on newer skeletons):
new OpenSky\LdapBundle\OpenSkyLdapBundle(),
app/config/config.yml. The exact key names drift between tags, so treat this as the shape to verify against the README of the tag you install:
opensky_ldap:
driver:
host: ldap.example.com
port: 389
base_dn: dc=example,dc=com
bind_dn: cn=svc-ldap,ou=services,dc=example,dc=com
bind_password: "%ldap_password%"
user:
base_dn: ou=people,dc=example,dc=com
filter: "(uid={username})"
attributes:
username: uid
email: mail
name: cn
security:
providers:
ldap:
id: opensky_ldap.user_provider
firewalls:
main:
pattern: ^/
form_login:
provider: ldap
login_path: /login
check_path: /login_check
logout: ~
If the bundle exposes a dedicated LDAP authentication provider service for your version, use it instead of plain form_login — plain form_login alone only validates against the user object, not the LDAP bind.ROLE_*) in the app rather than in LDAP.user.base_dn with the configured filter, then re-bind as the found DN with the submitted password to verify. This avoids fragile DN-string construction and works regardless of where in the tree the user lives.uid={username},ou=people,dc=example,dc=com), skip the service bind entirely. Faster, but breaks the moment a user moves OUs.User class via a factory/decorator rather than using the generic security User. This lets you persist locale, display name, and department on first login:
class LdapUserFactory
{
public function createUser(array $attributes)
{
$user = new User();
$user->setUsername($attributes['uid'][0]);
$user->setEmail($attributes['mail'][0] ?? null);
$user->setDisplayName($attributes['cn'][0] ?? null);
return $user;
}
}
ou=groups with (member={userDn}) and translate group CNs into ROLE_* inside an authentication success listener. Keep the mapping in config, not code, so ops can change it without a deploy.osixia/openldap or bitnami/openldap in Docker, seed an LDIF fixture (one user, one group, one service account), and point a test config at it. Treat the LDAP round-trip as an integration test, not a unit test.%ldap_password% from parameters.yml/.env; never commit the service-account password. Rotate it like any other credential.{username} substitution into (uid={username}) is an injection vector — *, )(, and \ need escaping. If the bundle doesn't do it, wrap the provider.LDAP_INVALID_CREDENTIALS, 49) from "service account can't bind" (also 49) and "server unreachable" (81). Log the LDAP error code; otherwise you'll debug the wrong layer.mail vs MAIL). Always index [0] and normalize keys in your factory.ldaps:// or StartTLS on 389, confirm the cert chain is trusted by PHP's CA store. If searches return partial results, referrals are likely being ignored — most clients need LDAP_OPT_REFERRALS disabled or followed explicitly.UsernameNotFoundException, not a null user that later explodes on getRoles().form_login_ldap/http_basic_ldap and the Symfony\Component\Ldap component; a migration is usually a day's work and removes an unmaintained dependency with zero downstream dependents. Budget for it rather than building new features on this bundle.How can I help you explore Laravel packages today?