yunai39/simple-ldap-bundle
A lightweight LDAP bundle for PHP applications that simplifies connecting to directory servers, authenticating users, and running common LDAP queries. It wraps PHP's LDAP extension with straightforward configuration for quick integration.
First, the mismatch: yunai39/simple-ldap-bundle is a Symfony bundle, not a Laravel package. It ships no service provider, no facade, no config/ publish tag, and no Laravel-compatible discovery. If you are building a Laravel app, you cannot composer require this and call it — you would be fighting Symfony's Kernel, bundles.php, firewall system, and event dispatcher. Treat it as reference material for LDAP auth logic, not a dependency.
If you're on a Symfony 2.x/3.x project that already uses it:
composer require yunai39/simple-ldap-bundle (pin the tag — see Gotchas).app/AppKernel.php: new Yunai39\Bundle\SimpleLdapBundle\SimpleLdapBundle().DependencyInjection/Configuration.php first — that file is the real documentation, since the README is minimal. It tells you the exact config tree (host, port, base DN, bind DN/password, user filter, attribute mapping).config.yml under simple_ldap: and wire it into a security.firewalls entry.If you're on Laravel: skip this. Use directorytree/ldaprecord-laravel or adldap2/adldap2-laravel. If you must borrow, read the bundle's user provider class and reimplement as a custom Illuminate\Contracts\Auth\UserProvider.
Where to look first in the repo: DependencyInjection/, Security/ (listener + user provider), and Resources/config/. Those three directories contain 90% of the behavior.
Bind-then-search (the standard pattern this bundle encodes):
(uid=%s) or (sAMAccountName=%s).User object keyed by the LDAP attribute you mapped (email, username, DN).Do not skip step 4 and just compare passwords against a search result — you must bind to actually authenticate.
Firewall wiring (Symfony):
security:
providers:
ldap:
simple_ldap: ~
firewalls:
main:
simple_ldap: ~ # the listener
form_login:
provider: ldap
Laravel translation of the pattern — implement Illuminate\Contracts\Auth\UserProvider:
retrieveById → search directory by DN or mapped ID.retrieveByCredentials → run the LDAP search filter, return a GenericUser or EloquentUser.validateCredentials → perform the rebind with the submitted password.Sync/local-hybrid pattern: LDAP for authentication, local DB for authorization. On first successful login, firstOrCreate the user locally, hash nothing (LDAP owns the password), and store the DN + mapped attributes. This avoids querying LDAP on every page load and decouples roles/permissions from directory schema.
Group/role mapping: Extend the user provider to also read memberOf (AD) or groupMembership (OpenLDAP) during the search, flatten to role names, and map to your app's roles. Do this in a decorator around the provider, not by editing the bundle — the bundle won't be maintained.
composer require yunai39/simple-ldap-bundle:1.0.* (or whatever the exact tag is). Without a pin, Composer may resolve to a dev-master that was never tagged.each(), create_function-era idioms, or non-return-typed signatures. Expect PHP 8 deprecations and possible fatals. Run composer why-not php 8.2 before committing.SimpleLdapBundle::boot() lifecycle, and security.yaml all assume Symfony. The realistic move is to read the ~3 relevant classes and port them, or use LdapRecord.\2a, \28, \29, \5c, \00). Verify the bundle escapes; older code often concatenates uid= values directly.CN=Smith, John,OU=...) must be escaped as \, when constructing the bind DN from a stored value. Test with names containing commas and +.ldap vs ldaps / StartTLS: plain ldap/389 sends the password in cleartext. Use ldaps/636 with a valid CA, or StartTLS with LDAP_OPT_X_TLS_REQUIRE_CERT. The bundle's config may not expose a StartTLS toggle — that's a common reason to fork.sAMAccountName (not uid), may need DOMAIN\user form on bind, and returns memberOf; OpenLDAP uses uid and needs groupMembership. The filter and base DN must be tuned per directory.ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7) gives verbose stderr traces; ldap_error($conn) after every call tells you whether you're getting Invalid credentials (49) (bad password), No such object (32) (bad base DN), or a TLS failure.security.authentication.provider / custom UserProviderInterface — wrap or replace the bundle's provider, keep the bundle for its connection glue, and don't touch vendor/ so updates (however rare) don't clobber your code.How can I help you explore Laravel packages today?