sroutier/eloquent-ldap
Bring Eloquent-style syntax to LDAP in Laravel: query and authenticate against directory servers like Active Directory or OpenLDAP using familiar model methods, so user lookups feel like any other database query.
Architecture fit. The package is narrow by design: it hooks Laravel's auth layer and adds a fallback path so that a failed Eloquent (local database) credential check is retried against a configured LDAP/AD server. It is a user-provider/guard extension, not an identity broker — there is no group-to-role mapping, no JIT provisioning, no directory sync, no password-change delegation. For a single Laravel monolith with an existing users table and a legacy AD estate, that shape is a reasonable 80% fit. For a multi-service estate or anything expecting directory-driven authorization, it duplicates identity logic per application and becomes a divergence source.
The architectural crux is the ordering: local DB first, LDAP second. That means a local password hash short-circuits Active Directory entirely. Disabled, terminated, or password-expired AD accounts with a surviving local hash remain valid; MFA, lockout policy, conditional access, and sign-in logging are all bypassed on the local path. This is a governance decision masquerading as a configuration default, and it is the single issue most likely to fail a security review.
Integration feasibility. Low effort to prototype, high effort to certify. The upstream dependency chain (adldap2 / adldap2-laravel) is archived and unmaintained, so the package is effectively pinned to PHP 7.x-era code and older Laravel majors (roughly 5.5–6.x). Running it on Laravel 10/11 and PHP 8.2 requires a fork with dependency overrides and a real test pass — which is no longer "adopting a package," it is "adopting a codebase."
Technical risk.
| Risk | Severity | Notes |
|---|---|---|
| GPL-3.0 license | High | Copyleft. Generally tolerable for internal-only use, a blocker for distributed/on-prem customer software. Needs counsel, not a TPM, to sign off. |
| Abandonment | High | 20 stars, 0 dependents, single maintainer, stale activity. No security patch path. |
| Shadow-identity class | High | Local-first fallback creates accounts that outlive their AD counterpart. |
| Silent degradation | Medium | LDAP outage is indistinguishable from a legitimate local login; failures are invisible. |
| Unmaintained transitive deps | Medium | Vulnerabilities land in adldap2, not in this package; you inherit them. |
| Security hygiene unknown | Medium | No visible evidence of enforced TLS/STARTTLS, bind-account handling, or timing-safe dual-path comparison (user enumeration). |
| Community signal | Low-Medium | Score 0.32 and zero dependents are consistent with "works for the author." |
Key questions for the team.
adldap2 transitive dependency acceptable or must it be replaced by LdapRecord?Stack fit. The package slots into config/auth.php as a custom user provider behind the standard Auth facade, so application code that calls Auth::attempt() is largely untouched. That is the attraction: a contained blast radius at the auth boundary. It assumes a conventional users table with email/username and password columns, and it assumes a directory schema close enough to AD for its attribute mapping to work without patching.
Options, ranked.
| Option | Effort | Verdict |
|---|---|---|
| A. Adopt as-is | Days | Reject — unmaintained, GPL, likely incompatible with current Laravel. |
| B. Fork and maintain | 3–6 weeks + ongoing | Viable only if the local-first semantics are genuinely wanted and no IdP is available. |
C. Reimplement the pattern on directorytree/ldaprecord-laravel |
2–4 weeks | Recommended. LdapRecord is actively maintained, MIT-licensed, and supports Laravel 10/11. Rebuild the fallback as ~150 lines of custom user provider on top of it. |
| D. External IdP (Entra ID / Keycloak) via SAML or OIDC | 4–8 weeks | Preferred if the AD estate is cloud-migrated or if MFA/conditional access is in scope. Sidesteps the package entirely. |
Option C captures nearly all of the package's value — the fallback logic is not the hard part — while eliminating the license, maintenance, and compatibility risks. The intellectual property worth keeping here is the pattern, not the code.
Migration path (assuming C).
ldap_dn / auth_source columns to users; backfill existing local accounts as local.Compatibility watch-list. Password reset (must not create local credentials for directory users), "remember me" tokens, session invalidation on AD disable, case sensitivity of usernames, and rate limiting on the LDAP path.
Sequencing. License review and security requirements sign-off gate everything else. Do those in week 0–1, in parallel with the technical spike — not after.
Maintenance. The package as-shipped has no maintenance story: no upstream patch stream, no CVE response, no Laravel major upgrades. If adopted via fork, the team inherits a small but permanent liability — roughly 10–20% of one engineer, plus a mandatory review at every Laravel upgrade. Option C shifts that to a maintained upstream and reduces the residual to a thin custom provider.
Support. There is no vendor, no active community, and no meaningful issue backlog activity. Troubleshooting falls entirely to whoever on the team understands LDAP bind semantics. Budget an internal SME; do not assume the package's documentation covers AD edge cases (referrals, nested groups, LDAPS certificate chains, service-account expiry).
Scaling. Each login is a bind against the directory — one network round-trip, plus a search if group resolution is involved. At hundreds of logins per minute this is fine; at thousands you need connection reuse, a bounded timeout (2–3s), and a circuit breaker so directory latency doesn't become auth latency. Directory-side rate limits and service-account lockout from failed binds are the realistic ceilings. Caching resolved user attributes with a short TTL is the standard mitigation.
Failure modes.
| Failure | Package behavior | Required mitigation |
|---|---|---|
| LDAP server unreachable | Silently falls back to local auth — looks like success | Emit a distinct metric/alert on fallback activation; do not treat as normal |
| Service bind account expired/locked | All directory logins fail; local logins succeed invisibly | Monitor bind account health proactively; separate alert channel |
| AD account disabled but local hash exists | Login succeeds via local path | Eliminate local hashes for directory users; periodic reconciliation job |
| Password expired in AD | Local path still authenticates | Same reconciliation; enforce expiry parity |
| Slow directory | Login latency spikes to the LDAP timeout | Explicit timeout, circuit breaker, async health check |
| Deprovisioning gap | Orphaned local credentials persist indefinitely | Scheduled reconciliation against AD; alert on mismatches |
The theme is that the fallback is the failure mode. Any design that continues on error must be louder, not quieter, than one that fails closed. If the business genuinely needs availability-over-correctness, then the fallback accounts must be a small, named, audited break-glass set — not every user with a local hash.
Ramp-up. A mid-level Laravel engineer with no LDAP background needs roughly 2–3 weeks to be productive on bind/search/AD-schema specifics, and another 1–2 weeks to be safe on TLS and failure handling. Security review adds a week. Realistic time-to-production for Option C, from kickoff and assuming the requirements are settled: 6–8 weeks including dual-run.
Recommendation. Do not adopt sroutier/eloquent-ldap as a dependency. The GPL-3.0 license, absent maintenance, and stale dependency chain are individually disqualifying and collectively decisive. Reimplement the fallback pattern on LdapRecord (Option C) or move to a standards-based IdP (Option D), and invert the authentication ordering to LDAP-authoritative with explicit, audited local break-glass accounts.
How can I help you explore Laravel packages today?