Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ldap Bundle Laravel Package

fr3d/ldap-bundle

Symfony bundle that adds LDAP authentication to your app. Integrates with Symfony Security, binds against LDAP servers, maps directory attributes to user objects, and supports configurable connections/domains for directory-backed login.

View on GitHub
Deep Wiki
Context7

Getting Started

FR3DLdapBundle is glue between Symfony's security firewall and an LDAP directory: it replaces the credential check with an LDAP bind, then uses your existing user manager (FOSUserBundle, a custom manager, etc.) to find, create, and update the local user record. It is not an LDAP client; the actual protocol work is delegated to a Zend/Laminas LDAP library.

Minimal setup on the versions v4.0.0 targets (PHP 7.1+, Symfony 3.4+):

  1. Install and pin the dependency. v4.0.0 is the first release that raises the floor to PHP 7.1 and Symfony 3.4, so anything on 2.8–3.3 must stay on the 3.x line:
    composer require fr3d/ldap-bundle:^4.0
    
    Run composer show afterwards to confirm which LDAP library came in transitively (zendframework/zend-ldap or laminas/laminas-ldap) and pin that explicitly too.
  2. Register the bundle in app/AppKernel.php:
    new FR3D\LdapBundle\FR3DLdapBundle(),
    
  3. Configure the driver and the user query:
    fr3d_ldap:
        driver:
            host: ldap.example.com
            port: 389
            # username/password: only needed if anonymous search is denied
            # bindRequiresDn: true
        user:
            base_dn: ou=people,dc=example,dc=com
            filter: (&(objectClass=person)(uid=%s))
            attributes:
                - { ldap_attr: mail,  user_method: setEmail }
                - { ldap_attr: cn,    user_method: setDisplayName }
    
  4. Provide the persistence seam. The bundle expects a service named fr3d_ldap.user_manager implementing FR3D\LdapBundle\Manager\UserManagerInterface. With FOSUserBundle this is a thin adapter, not an alias, because the interfaces differ:
    services:
        fr3d_ldap.user_manager:
            class: AppBundle\Ldap\FOSUserManagerAdapter
            arguments: ['@fos_user.user_manager', '@security.encoder_factory']
    
    namespace AppBundle\Ldap;
    
    use FR3D\LdapBundle\Manager\UserManagerInterface;
    use FOS\UserBundle\Model\UserManagerInterface as FOSUserManager;
    
    class FOSUserManagerAdapter implements UserManagerInterface
    {
        private $users;
    
        public function __construct(FOSUserManager $users)
        {
            $this->users = $users;
        }
    
        public function createUser()               { return $this->users->createUser(); }
        public function findUserByUsername($name)  { return $this->users->findUserByUsername($name); }
        public function supportsClass($class)      { return $this->users->supportsClass($class); }
        public function updateUser($user)          { $this->users->updateUser($user); }
    }
    
  5. Wire it into the firewall. The bundle exposes a user provider (fr3d_ldap.security.user.provider) and a firewall listener enabled with fr3d_ldap: ~:
    security:
        providers:
            fr3d_ldapbundle:
                id: fr3d_ldap.security.user.provider
            fos_userbundle:
                id: fos_user.user_provider.username
            chain_provider:
                chain:
                    providers: [fr3d_ldapbundle, fos_userbundle]
        firewalls:
            main:
                provider: chain_provider
                fr3d_ldap: ~
                form_login:
                    login_path: /login
                    check_path: /login_check
                logout: ~
    

First use case to target: an existing FOSUserBundle application where employees should log in with corporate credentials while local roles and profile data stay in your database. Verify the directory side with ldapsearch -x -H ldap://ldap.example.com -b "ou=people,dc=example,dc=com" "(&(objectClass=person)(uid=jdoe))" before touching Symfony config, then submit the login form and confirm a fos_user row is created or updated.

If you are upgrading a working 3.x install, do that login test on a branch before anything else: the only documented breaking change in 4.0 is in the form validator, so a form_login-only integration should survive, but you want the proof early.

Where to look first: the README's "Full example" section, DependencyInjection/Configuration.php for every available option, Ldap/LdapManager.php for the query/bind logic, Security/Authentication/Provider/LdapAuthenticationProvider.php for the authentication flow, and Manager/UserManagerInterface.php for the persistence contract. When upgrading, read the release notes and the form validator class side by side — that is where the Symfony 3.4 class swap landed.

Implementation Patterns

Upgrading 3.x to 4.0. Treat this as a dependency-floor release, not a feature release. Before bumping, run composer why-not fr3d/ldap-bundle ^4.0 to see exactly which of your pinned PHP or Symfony constraints block it; the failure will be at solve time, and the message is usually clearer than the alternative of unpicking a half-upgraded composer.lock. The one code-level break is the form validator now targeting Symfony 3.4 form classes — if you extended, decorated, or overrode it, re-test that path specifically. If you only use the firewall listener with form_login, the config is unchanged: bump, clear the cache, log in.

Adapter over your user manager. The UserManagerInterface implementation is the only place your domain code should live. Keep it thin: delegate create/find/update, and put profile enrichment elsewhere. This is also the seam that lets you swap FOSUserBundle for a custom entity later.

Chain provider for hybrid account populations. Put the LDAP provider and your local provider behind a chain provider so service accounts, break-glass admins, and pre-provisioned local users still work. Be aware that ordering affects which authentication provider is consulted first: a user who exists in both LDAP and the local database will be authenticated against LDAP unless you deliberately restrict the firewall or listener.

Attribute mapping as a sync contract. Use user.attributes only for identity/profile fields where LDAP is authoritative (email, display name, employee ID). Roles, preferences, and application state should stay local. Guard setters, since a missing LDAP attribute can be passed as null, and a multi-valued attribute arrives as an array.

Active Directory specifics. AD almost always needs filter: (&(objectClass=user)(sAMAccountName=%s)), username_attribute: sAMAccountName, and a service-account bind for the initial search (driver.username / driver.password, plus bindRequiresDn: true if you must bind by DN). For OpenLDAP the common shape is (uid=%s) with bindRequiresDn: false. Keep these differences in environment-specific config, not in code.

Roles from LDAP. There is no built-in group-to-role mapping. The pragmatic pattern is to map memberOf to a single user field (a custom setLdapGroups method, or a custom attribute mapping that stores the array), then compute roles in getRoles() or in a security event subscriber after login. Avoid calling LDAP again on every request.

Testing. Mock FR3D\LdapBundle\Driver\LdapDriverInterface for unit tests of the manager and the authentication provider. For integration tests, run osixia/openldap in Docker, load an LDIF fixture, and point fr3d_ldap.driver.host at it via a test-specific config. Keep those tests in a separate suite so CI without a directory still passes. Pin the PHP/Symfony matrix explicitly now that 4.0 has a hard floor — a matrix entry on Symfony 3.3 will fail to install the bundle rather than fail a test.

Local development. Run OpenLDAP locally and seed a couple of users. Use ldapsearch and ldapwhoami to prove the filter and bind work, and only then exercise the firewall. This turns "Invalid credentials" from a Symfony debugging problem into a directory debugging problem.

Migration path. v4.0.0 modernizes the dependency floor but still does not reach Symfony 4+; the bundle's authentication-provider wiring no longer exists there. The native replacement is Symfony's ldap user provider with form_login_ldap, or a custom Authenticator using Laminas\Ldap. Landing on 3.4 LTS with 4.0 is the cleanest springboard for that rewrite — your adapter's find/create/update logic and your attribute-mapping decisions transfer directly; only the security wiring is replaced.

Gotchas and Tips

  • Maintenance is sparse, not guaranteed. v4.0.0 refreshes the dependency stack (PHP >=7.1, Symfony >=3.4) but adds no Symfony 4/5 support. Since the supported Symfony line tops out at 3.4, PHP 8 remains effectively out of reach, and you should still budget for the security-layer rewrite rather than assume a drop-in future upgrade.
  • The 4.0 breaking change is the form validator. It was moved onto Symfony 3.4 form classes. If you subclassed it, bound it in a form type, or asserted on its constraint messages, that is the code to re-test. Custom validator service definitions may also need re-tagging since the underlying parent classes changed.
  • Cross the 3.x/4.0 boundary deliberately. Keep old applications constrained to ^3.0 explicitly. A floating constraint will attempt 4.0, fail on the PHP or Symfony floor, and surface as a confusing install error rather than an obvious version mismatch. Pin bundle, LDAP library, and PHP version together in CI.
  • Filter escaping. Confirm how %s is interpolated before you trust it. If the username is sprintf'd straight into the filter without Ldap\Filter::escape() or ldap_escape(), characters like *, ), and \ become an LDAP injection surface. Test with hostile usernames, and override LdapManager if needed.
  • Password semantics. LDAP is the credential store; never persist the LDAP password locally. FOSUserBundle still wants a non-null password column, so set a random encoded value in the adapter and make sure there is no second local-password login path for LDAP-backed accounts.
  • Case sensitivity and DN normalization. AD is case-insensitive; OpenLDAP often is not. If findUserByUsername returns null after a successful bind, check casing and whitespace on the username before assuming the directory query is wrong.
  • Recreation on refresh. The token refreshes the user through the provider on each request. If a local user is deleted but still exists in LDAP, the create-on-login path may recreate it. Decide deliberately whether that is desirable.
  • Missing or multi-valued attributes. A missing attribute can call a setter with null; memberOf and similar attributes arrive as arrays. Both cases need explicit handling in your entity or adapter.
  • Config is compile-time, connections are lazy. Clear the cache after config changes — and again after the 4.0 upgrade, since validator metadata is cached alongside the container. A bad host or port will not fail at boot; it fails at the first login attempt, which makes it look like an authentication bug.
  • Opaque error messages. Symfony returns "Invalid credentials" regardless of whether the DN was not found, the bind failed, or the filter was malformed. Catch LdapException, log the underlying message in dev.log, and reproduce the exact query with ldapsearch.
  • TLS and certificates. Use ldaps:// on 636 or useStartTls: true; certificate validation issues surface as generic connection failures. Keep directory service credentials out of VCS via %env()% parameters.
  • Extension points. Swap fr3d_ldap.ldap_driver for a custom LdapDriverInterface, decorate fr3d_ldap.ldap_manager, or override the fr3d_ldap.security.user.provider and authentication provider service definitions. A compiler pass is the cleanest way to adjust service definitions without forking the bundle's config.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager