Every enterprise application eventually runs into the same requirement: “Integrate it with Active Directory.” It sounds simple. In reality, LDAP integration is full of pitfalls that can cost you weeks of debugging. Here is our battle-tested guide.
Active Directory — More Than a Directory Service¶
Active Directory (AD) is the backbone of identity management in most Czech corporations. Windows Server 2012 R2, the current version, combines an LDAP directory, Kerberos authentication, DNS services, and Group Policy into one whole. For web application developers, the LDAP interface is the primary entry point.
But beware — AD is not a pure LDAP server. Microsoft added its own extensions, non-standard attributes, and behavior that differs from OpenLDAP or 389 Directory Server. If you don’t know this, you debug.
Authentication — Bind Correctly¶
LDAP authentication works via the “bind” operation — the client sends a DN (Distinguished Name) and password, and the server verifies them. In AD you have three ways to specify identity:
# 1. DN bind (standard LDAP)
CN=Jan Novák,OU=Users,DC=corp,DC=example,DC=cz
# 2. UPN bind (Active Directory specific)
[email protected]
# 3. Down-level logon (NTLM style)
CORP\jan.novak
We recommend UPN bind — it’s the cleanest, doesn’t require knowing the exact DN (which changes when a user moves between OUs), and works consistently across domains in a forest.
Searching for Users — LDAP Filters¶
After a successful bind, you need to find the user and their attributes. LDAP filters have a specific syntax that you must know:
# Find user by sAMAccountName
(&(objectClass=user)(objectCategory=person)(sAMAccountName=jnovak))
# Find all active users in an OU
(&(objectClass=user)(objectCategory=person)
(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
# Find group members
(&(objectClass=user)(memberOf=CN=Admins,OU=Groups,DC=corp,DC=example,DC=cz))
Key detail: userAccountControl is a bit field. The filter with OID 1.2.840.113556.1.4.803 is a bitwise AND — it tests whether bit 2 (ACCOUNTDISABLE) is set. Without this filter, you’ll return disabled accounts too. A common mistake.
Groups and Authorization¶
AD groups are the foundation of authorization, but they have their specifics:
- Nested groups — a group can be a member of another group. The memberOf attribute shows only direct membership, not transitive.
- LDAP_MATCHING_RULE_IN_CHAIN (OID 1.2.840.113556.1.4.1941) — an AD extension for recursive membership lookups
- Primary group — typically Domain Users, not in memberOf! You must check primaryGroupID separately.
- Token bloat — a user in too many groups can have problems with the Kerberos token (max 65535 bytes)
# Recursive membership — find all (including indirect) group members
(&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=App-Admins,OU=Groups,DC=corp,DC=example,DC=cz))
LDAPS — Encryption Is a Must¶
LDAP without encryption sends passwords in plaintext. That’s unacceptable. You have two options:
- LDAPS (port 636) — SSL/TLS wrapper, requires a certificate on the domain controller
- StartTLS (port 389) — upgrading an existing connection to TLS
In practice, we recommend LDAPS. StartTLS has known implementation issues in some Java LDAP libraries and older AD versions. The certificate on the DC should be signed by an internal CA — if you don’t have a PKI infrastructure, now is the time to build one.
Connection Pooling and Performance¶
An LDAP bind is a relatively expensive operation. For a web application with dozens of requests per second, you don’t want to perform a bind for every request. Solutions:
- Service account bind — the application authenticates with a service account and then searches for users
- Connection pool — maintain a pool of open LDAP connections (Spring LDAP, Apache Directory API)
- Cache — cache LDAP query results with a reasonable TTL (5–15 minutes)
- Paging — for large result sets, use Simple Paged Results Control (1000 records per page)
Most Common Mistakes¶
Over years of AD integration, we’ve seen these mistakes repeatedly:
- Referral chasing — AD returns referrals to other domain controllers. If you don’t follow them, you’re missing users from child domains.
- Escaping special characters — a DN containing commas, quotes, or backslashes must be properly escaped. Otherwise: LDAP injection.
- Anonymous bind — modern AD disables it by default. Always use a service account.
- Hardcoded Base DN — instead, use RootDSE for automatic discovery of defaultNamingContext
- Ignoring empty password strings — some LDAP libraries perform an anonymous bind instead of returning an error. Always validate input.
The Path to SSO — SAML and Kerberos¶
Plain LDAP authentication requires the user to enter their password into your application. That’s not ideal — the password passes through an application that doesn’t need it. Better approaches for 2014:
- Kerberos/SPNEGO — transparent SSO for users logged into the domain. The browser sends a Kerberos ticket, the application verifies it.
- ADFS + SAML 2.0 — federated SSO via Active Directory Federation Services. Suitable for web applications and cloud services.
- Hybrid — LDAP for authorization (groups, attributes), Kerberos/SAML for authentication
AD Integration — An Investment in Foundations¶
Proper integration with Active Directory is not a glamorous project. You can’t show it at a board meeting and it won’t win any innovation awards. But it’s the foundation on which the identity management of the entire organization rests. Do it once and do it right — service account, LDAPS, connection pool, correct filters, and a path to SSO. Your users (and your ops team) will thank you.
Need help with implementation?
Our experts can help with design, implementation, and operations. From architecture to production.
Contact us