wiser/ldap-user-provider-bundle
A Symfony bundle that supplies an LDAP user provider so applications can authenticate against directory servers and load user records from LDAP. It reduces custom integration code by wiring user lookup into Symfony's security layer with minimal configuration.
Architecture fit — poor. Despite both being PHP, wiser/ldap-user-provider-bundle is a Symfony bundle, not a Laravel package. It targets Symfony 6.4's Security component (Symfony\Component\Security\Core\User\UserProviderInterface), Symfony's DependencyInjection container, its Bundle registration lifecycle, and the security.yaml user-provider wiring. Laravel has no bundle system, no Symfony Security component, and no security.yaml. Laravel's authentication contract is Illuminate\Contracts\Auth\UserProvider (with retrieveById, retrieveByCredentials, validateCredentials, updateRememberToken). The two interfaces are structurally similar but semantically and method-signature incompatible. "Integration" here means porting logic, not installing a dependency.
Integration feasibility — low. Laravel does ship several Symfony components transitively (HttpFoundation, Console, Finder, EventDispatcher, Routing, Translation, etc.), but it does not ship symfony/security-bundle, symfony/security-core, or symfony/http-kernel's bundle kernel. Pulling those in to satisfy a single LDAP user provider would drag the Symfony security subsystem into a Laravel app that already has a mature, opinionated auth layer — a net-negative architectural trade. The only reusable artifact is the internal LDAP query/filter/mapping logic (binding, search filter construction, DN resolution, attribute-to-user hydration), which is typically 200–400 lines and can be lifted into a custom Laravel UserProvider or guard in a day or two.
Technical risk — high, concentrated in three areas:
UserInterface vs. Laravel Authenticatable; Symfony PasswordAuthenticatedUserInterface vs. Laravel's getAuthPassword()/hasher pipeline. Any adapter layer becomes bespoke glue that the upstream project will never validate.Key questions before any decision:
sAMAccountName, paged searches, LDAP signing requirements) vs. OpenLDAP/FreeIPA materially changes the filter and attribute mapping.users table with linkage? This determines whether you need a provider, a guard, or a sync job.Recommendation: Do not adopt as a dependency. Treat it as a reference implementation to read, and build a ~1–2 day Laravel-native provider against LdapRecord-Laravel (actively maintained, first-class Laravel integration, AD/OpenLDAP support, Eloquent-style models and query builder) or a thin ldap_* / Symfony\Component\Ldap-backed custom provider.
Stack fit. Target a native Laravel auth integration:
| Concern | Recommended component |
|---|---|
| LDAP transport & directory abstraction | directorytree/ldaprecord-laravel (wraps ldaprecord with an Eloquent-like model layer) |
| Auth contract | Custom Illuminate\Contracts\Auth\UserProvider or a Guard registered via Auth::extend() |
| Credential validation | LDAP bind-as-user; do not mirror passwords locally |
| Attribute/role sync | Queued job writing to users / roles tables; LDAP as read authority |
| Config | config/ldap.php + .env — no YAML, no bundle registration |
Migration path from the bundle. The bundle's value is concentrated in its filter construction and user hydration. Extract, don't depend:
loadUserByUsername / refreshUser implementations and the search-filter builder.LdapRecord model scope (Model::where('samaccountname', '=', $username)->first() or a raw ldap_search filter for OpenLDAP).$fillable/accessor translation, not Symfony UserInterface hydration.Configuration tree, and event subscribers entirely — Laravel's container resolves the provider by binding, and events map to native Laravel events.Compatibility. The bundle's Symfony 6.4 constraint is a hard ceiling: no Laravel version officially supports Symfony Security 6.4's auth APIs, and Laravel's own Symfony component constraints are pinned per major (Laravel 11 ships Symfony 7.x components as of ~2024/25). Installing it would produce dependency-resolution conflicts or, at best, an unused subtree. There is no "supported" combination — this is the single most decisive fact in the evaluation.
Sequencing (if pursuing LDAP auth in Laravel):
retrieveByCredentials = bind-as-user + directory search; retrieveById = local lookup or cached DN lookup; validateCredentials = successful bind. Register via Auth::provider().Maintenance — you own it either way. With the bundle, you own a forked Symfony provider inside a Laravel app that will never receive upstream fixes relevant to your runtime; you inherit the maintenance burden and the impedance mismatch. With a native provider, you own ~150–300 lines of Laravel code plus a well-maintained dependency. The latter is strictly less work and far more legible to a Laravel-fluent team. The 2-star/0-dependent profile means there is no realistic chance of upstream responding to issues, and Apache-2.0 permits forking but offers no maintenance transfer.
Support. No vendor support, no community, no issue-response expectation. Your escalation path is the source code and your own team. Contrast with the native path, where ldaprecord has documentation, an active issue tracker, and a Laravel-shaped mental model that new hires already recognize. Budget for the LDAP directory side too — AD/OpenLDAP operational expertise is the actual scarce skill, not the PHP glue.
Scaling. Authentication traffic is bound by directory round-trip latency (typically 5–50 ms LAN, much worse cross-region) and by your directory's connection limits. Two scaling obligations:
Cache::remember, short TTL) to avoid a directory hit on every token refresh, but never cache the credential-validation bind itself.
Also watch pagination — AD caps results at 1000 by default, so group enumeration must use paged results or LDAP_MATCHING_RULE_IN_CHAIN (1.2.840.113556.1.4.1941) for nested groups. Both are implementation concerns the bundle does not resolve for you.Failure modes to design for explicitly:
LDAP_OPT_NETWORK_TIMEOUT and request timeouts; a hung bind will otherwise consume PHP-FPM workers and cascade into a full-app outage.Ramp-up. A Laravel team reads a native UserProvider + guard registration in an hour. The Symfony bundle requires the team to hold two mental models simultaneously (Laravel auth, Symfony Security) to debug a single login path — a persistent tax on every on-call rotation. For a package with 2 stars and zero dependents, that tax buys nothing.
Verdict: Low opportunity score (19.47) is consistent with the technical reality. Skip the dependency; mine the filter logic if useful, and ship a native Laravel provider on ldaprecord-laravel. Estimated delta: ~2–3 days of build versus indefinite ownership of an unsupported cross-framework shim.
How can I help you explore Laravel packages today?