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 Laravel Package

symfony/ldap

LDAP client for PHP built on the ldap extension, providing connection, querying, and entry management since Symfony 3.1. Stable and maintained within the Symfony ecosystem.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • This is a low-level LDAP client, not an authentication framework. It gives you Ldap::create(), Adapter/Connection, Query, Entry, and Collection — clean primitives for bind/search/read/write. Everything Laravel expects from an auth backend (Guard, UserProvider, password broker, remember-me, session lifecycle) has no counterpart here. The Symfony LdapUserProvider and Security bundle wiring that make this package productive in Symfony are not installed and not usable in a Laravel app.
  • Consequence: the integration surface is a custom App\Services\Ldap\LdapClient wrapper plus an Illuminate\Contracts\Auth\UserProvider implementation. The package carries roughly 20% of the effort; the Laravel-side bridge carries the remaining 80%.
  • The component is intentionally framework-agnostic (it only needs the PHP ldap extension plus a very small Symfony dependency footprint), so it drops into a Laravel service container cleanly — you bind the Adapter as a singleton in a dedicated service provider and inject it. There is no package auto-discovery, no config publishing, no facade. That is not a defect for a component, but it means our integration is bespoke code we own and test.

Integration feasibility

  • High for connectivity, medium for authentication. Reading entries, validating credentials via bind, and enumerating groups is straightforward and well documented. Turning that into Laravel login, just-in-time user provisioning, and group→role mapping is design work we must specify.
  • The Adapter is resettable (bug fix landed in 6.4.36 / 7.4.8 / 8.0.8) — this matters for Laravel Octane and long-lived queue workers, where a stale bind or dead socket otherwise poisons the worker until restart.
  • Credential semantics need explicit handling: an empty-password bind can succeed as an anonymous bind, which is a well-known LDAP auth bypass. The package will not save us from that; our provider must reject blank passwords before calling bind.
  • Version alignment is the main friction. Symfony 8.x requires PHP ≥ 8.4 and removes AbstractQuery::setSizeLimit(); 7.x requires PHP ≥ 8.2; 6.4 is the LTS line. Our Symfony transitive dependency major (pulled in by Laravel itself via console/http-foundation) should match the symfony/ldap major we choose to avoid resolver conflicts and duplicate component trees.

Technical risk

  • ext-ldap is a hard, non-negotiable requirement. Many shared hosts disable it, and slim/Alpine containers need openldap-dev at build time and the extension compiled or installed. If any target environment (local, CI, a customer's on-prem deploy) lacks it, the package is unusable regardless of code quality. This is the single largest adoption risk and must be verified per environment before commitment.
  • Metrics here are misleading and should not drive the decision. Stars: 135 and Dependents: 0 describe a read-only split repository; development, issues, and PRs live in the symfony/symfony monorepo, so GitHub-level popularity signals are structurally understated. Conversely, the package is flagged as "looking for a backer," and support is community-only (MIT, no vendor SLA).
  • Abstraction leakage. Server controls (paged results, whoami, SASL/EXTERNAL), referral chasing, and AD-specific behaviors like nested group expansion are only partly covered — SASL bind and whoami were added in 7.2, paging is not a first-class API. Any of these becomes a drop to native ldap_* calls, which undermines the reason for adopting the package. Directorys with >1000 entries will hit AD's default size limit on search and need paging work.
  • Operational failure semantics are ours to define. The library raises exceptions for connection vs. bind failures; mapping "wrong password" (401, count it) versus "directory unreachable" (503, do not count it) is application logic. Getting it wrong produces account lockouts from retry storms or spurious lockouts of valid users.
  • TLS is partly outside PHP. LDAPS vs StartTLS, CA trust, and certificate validation are governed by ldap.conf/openssl configuration and the LDAP_OPT_* options we set. This is infrastructure configuration, not something the package handles.

Key questions

  1. Is the real requirement credential validation against a directory, or SSO? LDAP bind is not SSO. If the business goal is "no second password," SAML/OIDC (Entra ID, Okta) is the correct answer and this package is a detour.
  2. Does every target runtime (dev, CI, staging, prod, and any customer-hosted deployment) have ext-ldap enabled? Who verifies this, and by when?
  3. Which directory and schema: Active Directory (sAMAccountName, memberOf, nested groups) or OpenLDAP/389DS? Group→role mapping rules must be specified by the business, not inferred by engineering.
  4. Do we bind as the user (simple, no service account, but no pre-auth search) or as a service account then search then re-bind (needed for group lookup, requires a privileged credential in our secrets store)?
  5. Who owns the LDAP integration long-term — is there a named internal owner, given community-only upstream support?
  6. Do we need just-in-time user provisioning into Eloquent, or is LDAP always the source of truth on every request (latency and availability implications)?
  7. Have we evaluated directorytree/ldaprecord-laravel as a Laravel-native alternative with a higher-level, Eloquent-shaped API and AD-friendly conventions? We should be able to articulate why we chose the lower-level component.

Integration Approach

Stack fit

  • Wrap, don't sprinkle. Create a single App\Ldap\LdapClient interface (bind, authenticate, findByUsername, groupsFor, search) and one SymfonyLdapClient implementation backed by the Symfony Adapter. Bind it as a singleton in LdapServiceProvider, configured from config/ldap.php fed by env vars (LDAP_HOST, LDAP_PORT, LDAP_ENCRYPTION, LDAP_BASE_DN, LDAP_SERVICE_DN, LDAP_SERVICE_PASSWORD, LDAP_TIMEOUT). Never let Symfony\Component\Ldap\* types appear in controllers, models, or jobs.
  • Implement Illuminate\Contracts\Auth\UserProvider (and optionally a custom Guard) on top of that interface. First login performs search-then-bind, maps the entry to an Eloquent users row (JIT provisioning), syncs group→role assignments, and thereafter authenticates against the local record or re-binds depending on the policy we pick.
  • Reuse Laravel's native AuthenticatesUsers/Fortify flow rather than reinventing login. Add ldap as an additional guard; keep the existing eloquent guard as a break-glass fallback for local admin accounts.
  • Do not reach for Socialite — it is OAuth, not LDAP. If the org later wants true SSO, that is a separate SAML/OIDC track.

Migration path

  • Phase 0 — abstract first: if any ad-hoc ldap_* calls exist today, move them behind the LdapClient interface with no behavior change. This decouples the eventual library swap from the auth work and gives us a rollback seam.
  • Phase 1 — replace internals with the Symfony adapter inside the existing interface. Behavior and tests stay green.
  • Phase 2 — introduce the UserProvider and JIT provisioning behind a feature flag; run LDAP and local auth in parallel.
  • Phase 3 — flip the default guard, keep local fallback, and decommission the legacy path after a full cycle (one password-rotation window is a reasonable bar).
  • If we are migrating off an abandoned package (Adldap2-style) or a bespoke implementation, treat the LdapClient interface as the contract that makes either direction reversible.

Compatibility

  • Choose the Symfony major that matches the PHP floor we support and the Symfony major Laravel already pulls in: 6.4 LTS for PHP 8.1+/Laravel 10-era stacks, 7.x for PHP 8.2+ (Laravel 11/12), 8.x only if we are on PHP 8.4+. Pin explicitly in composer.json and add a CI matrix job per supported PHP version.
  • Track the 8.0 removals as a future upgrade item: AbstractQuery::setSizeLimit() removed, eraseCredentials() removed from security interfaces, PHP 8.4 floor. None block adoption on 7.x today.
  • Ensure LDAP server compatibility is stated explicitly (AD 2016+, OpenLDAP 2.4+, 389DS) and verified in a spike rather than assumed.

Sequencing

  1. Environment proof (½ sprint): install ext-ldap in Docker/CI/staging; smoke-test connect + bind + search against a containerized OpenLDAP and against the real corporate directory with a service account.
  2. Provider + config + health check (1 sprint): service provider, config file, ldap:health artisan command, structured logging and metrics for connect latency, bind success/failure, search duration.
  3. Authentication path (1 sprint): search-then-bind, blank-password guard, exception taxonomy (invalid credentials → 401; unreachable/timeout → 503), rate limiting and per-account lockout protection on repeated failures.
  4. Provisioning + authorization (1 sprint): JIT user creation, group→role mapping, deprovisioning/disable policy for users removed from the directory.
  5. Test harness: ldap service in docker-compose and CI, fixture LDIFs, integration tests that exercise paging and nested groups rather than only happy-path binds.
  6. Rollout: feature flag, parallel-run with local auth, documented break-glass procedure, then default-guard cutover.

Operational Impact

Maintenance

  • Upstream churn is low and predictable: Symfony's deprecation policy gives one major cycle of warning before removal, and the recent releases are dominated by small bug fixes (socket timeout casting, adapter reset, extension deprecation) rather than redesign. That is a good long-term signal for a component of this kind.
  • But the code we own is the Laravel bridge, not the package. Budget ongoing ownership for the UserProvider, group-mapping rules, and the secrets rotation for the service-account bind — realistically a few days per quarter plus an upgrade task per Symfony major.
  • The split repo means contributions must go through symfony/symfony PRs. If we need an upstream fix, our lead time depends on Symfony's community review, not on a vendor. Plan to work around issues locally rather than block on upstream.

Support

  • MIT, community-only, no SLA, and the project is explicitly seeking a backer. There is no commercial escalation path. Documented Symfony docs and the issue tracker are good quality, but if this integration is on the critical login path, we need an internal owner with LDAP literacy and a named backup.
  • Worth an explicit comparison note in the ADR: directorytree/ldaprecord-laravel offers a Laravel-native, Eloquent-shaped API and commercial support options, at the cost of less direct control and a different abstraction. Choosing the Symfony component is a defensible decision for control and Symfony-ecosystem alignment, but it must be a stated trade-off, not a default.
  • Correct a claim from the product assessment before it reaches stakeholders: this package does not provide connection pooling. PHP-FPM's process-per-request model gives per-request connections; under Octane the singleton adapter must be reset or scoped per request. Do not cite pooling as a scalability benefit.

Scaling

  • LDAP search latency (5–50 ms typical, worse over WAN to on-prem AD) sits directly on the login critical path. Cache the resolved user and group membership in Laravel (with a short TTL and explicit invalidation on re-bind) so we do not query the directory on every request. Never perform an LDAP search inside a request-scoped authorization check.
  • Paged searches are mandatory for any base DN with more than the server's size limit (AD defaults to 1000). Plan the paging strategy at design time; the component gives limited first-class support, so this may be a native ldap_* fallback.
  • Bound concurrency: a singleton adapter per worker, an explicit LDAP_OPT_NETWORK_TIMEOUT and default_socket_timeout, and a hard cap on retries. Under Octane, reset the adapter after bind failures and on a schedule.
  • For large group trees, prefer resolving membership via the user's memberOf (AD) over recursive group expansion, and cache the flattened role set.

Failure modes

  • Directory unreachable / timeout: must surface as 503 and fall back to local auth where policy allows. Without this, a directory outage becomes a total login outage.
  • Invalid credentials vs. infra error: must be distinguishable in the exception handler, logged differently, and counted differently — otherwise we either lock out valid users or mask outages as bad passwords.
  • Account lockout: repeated bind attempts against AD's lockout policy can lock users out of everything, not just our app. Require per-account attempt throttling and never retry a failed user bind.
  • Anonymous-bind bypass: empty password must be rejected before the bind call. Add an explicit test.
  • TLS/certificate failures: surface as connection errors, not auth errors; pin CA configuration and document ldap.conf requirements as deployment prerequisites.
  • Partial results / silent truncation: a search hitting the size limit returns success with incomplete data. Detect by requesting one more entry than expected and by logging result counts.
  • Stale state under Octane/queues: dead sockets and bound-as-another-user state; mitigate with the resettable adapter and periodic reconnect.
  • Service-account expiry: a rotated or expired service password breaks group lookups while user binds still work — a confusing partial-failure mode. Alert on search-with-service-account failures specifically.

Ramp-up

  • Expect 2–4 days for a mid-level Laravel developer to be productive with LDAP concepts (DNs, bind semantics, RFC 4515 filter escaping — the component ships an Escaper, so use it), and roughly one sprint end-to-end for a production-grade bridge including tests and observability.
  • The real ramp-up cost is not the API; it is directory-specific behavior (AD nested groups, referral handling, lockout policy, TLS trust). Budget for access to a directory administrator during the build, and for a containerized LDAP fixture in CI so the integration is testable without touching production AD.
  • Onboarding doc must cover: env vars, break-glass local login, ldap:health, where group→role rules live, and who to escalate to when the directory is down.
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.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk