LDAPts Client
LDAPts is a modern, TypeScript-first LDAP client library for Node.js, providing a robust API to interact with LDAP directory servers, including Active Directory. It supports both `ldap://` and `ldaps://` protocols, offering comprehensive options for secure connections using TLS. The library is currently at version 8.1.7 and maintains an active release cadence, frequently publishing bug fixes and minor enhancements. Key features include client creation with configurable timeouts, strict DN parsing, and support for various LDAP operations such as bind, add, compare, delete, modify, and search. It differentiates itself by offering full TypeScript type definitions out-of-the-box, targeting recent Node.js versions (>=20), and internally handling BER encoding/decoding, reducing external dependencies.
Common errors
-
LDAP operation failed: LDAP_INVALID_CREDENTIALS
cause The Distinguished Name (DN) or password provided for the bind operation is incorrect.fixVerify the `bindDN` and `bindPassword` are correct for the LDAP server. For Active Directory, the `bindDN` is often the User Principal Name (UPN) or `sAMAccountName` in a specific format. -
LDAP operation failed: connect ECONNREFUSED
cause The client failed to establish a TCP connection to the LDAP server, often because the server is not running, is unreachable, or the port is blocked by a firewall.fixEnsure the LDAP server is online and listening on the specified host and port. Check network connectivity between your application and the server, and verify any firewall rules. -
LDAP operation failed: ETIMEDOUT
cause Either the TCP connection attempt or an LDAP operation timed out before a response was received.fixIncrease `connectTimeout` for connection issues or `timeout` for operation issues in the `Client` constructor options. Also, check network latency or server load. -
LDAP operation failed: InvalidAsn1Error: invalid BER: expected BER length to be less than or equal to the amount of data in the buffer.
cause The LDAP server responded with malformed BER-encoded data, indicating an issue with the server's response or a parsing error in the client.fixThis often points to an issue with the LDAP server itself returning non-standard or corrupt data. Review the LDAP server logs for errors related to the request. Ensure `ldapts` is up to date, as parsing bugs are sometimes fixed in new versions.
Warnings
- breaking LDAPts now strictly requires Node.js version 20 or higher. Older Node.js versions are not supported and may lead to runtime errors or installation issues.
- gotcha The `strictDN` option defaults to `true` in client configuration. This enforces strict DN parsing for client methods, which might cause issues if your application interacts with LDAP servers that return or expect less formally structured distinguished names.
- breaking The internal `asn1` package dependency has been replaced with an internal BER module since v8.1.1. While this improves maintainability and reduces external dependencies, direct reliance on `asn1` exports or specific behaviors from previous versions will no longer work.
- gotcha The client configuration's `tlsOptions` should always specify a `minVersion` (e.g., 'TLSv1.2') and `rejectUnauthorized: true` in production environments for secure connections. Using outdated TLS versions or disabling certificate validation can expose your application to security vulnerabilities.
Install
-
npm install ldapts -
yarn add ldapts -
pnpm add ldapts
Imports
- Client
const { Client } = require('ldapts')import { Client } from 'ldapts' - Control
const { Control } = require('ldapts/controls')import { Control } from 'ldapts/controls' - PagedResultsControl
const { PagedResultsControl } = require('ldapts/controls')import { PagedResultsControl } from 'ldapts/controls' - BerReader
const { BerReader } = require('ldapts')import { BerReader } from 'ldapts' - InvalidAsn1Error
const { InvalidAsn1Error } = require('ldapts')import { InvalidAsn1Error } from 'ldapts'
Quickstart
import { Client } from 'ldapts';
async function connectAndSearch() {
const ldapUrl = process.env.LDAP_URL ?? 'ldap://localhost:389';
const bindDN = process.env.LDAP_BIND_DN ?? 'cn=admin,dc=example,dc=com';
const bindPassword = process.env.LDAP_BIND_PASSWORD ?? 'password';
const searchBase = process.env.LDAP_SEARCH_BASE ?? 'dc=example,dc=com';
const searchFilter = process.env.LDAP_SEARCH_FILTER ?? '(objectClass=*)';
const client = new Client({
url: ldapUrl,
timeout: 5000, // Milliseconds client should let operations live for
connectTimeout: 5000, // Milliseconds client should wait for TCP connection
tlsOptions: {
minVersion: 'TLSv1.2', // Enforce minimum TLS version
rejectUnauthorized: false // Set to true in production with valid certs
},
strictDN: true
});
try {
console.log(`Attempting to bind as ${bindDN} to ${ldapUrl}...`);
await client.bind(bindDN, bindPassword);
console.log('LDAP bind successful!');
console.log(`Performing search under '${searchBase}' with filter '${searchFilter}'...`);
const { searchEntries, searchReferences } = await client.search(
searchBase,
{
filter: searchFilter,
scope: 'sub',
attributes: ['dn', 'cn', 'mail'], // Request specific attributes
sizeLimit: 10 // Limit results for example
}
);
if (searchEntries.length > 0) {
console.log(`Found ${searchEntries.length} entries.`);
searchEntries.forEach(entry => {
console.log(`- DN: ${entry.dn}, CN: ${entry.cn ?? 'N/A'}, Mail: ${entry.mail ?? 'N/A'}`);
});
} else {
console.log('No entries found.');
}
console.log('Unbinding from LDAP server...');
await client.unbind();
console.log('Unbind successful.');
} catch (error: any) {
console.error('LDAP operation failed:', error.message);
// Specific error handling for common LDAP issues
if (error.code === 'ETIMEDOUT') {
console.error('Timeout occurred. Check network connectivity or server responsiveness.');
} else if (error.code === 'LDAP_INVALID_CREDENTIALS') {
console.error('Invalid credentials provided for bind operation.');
} else if (error.message.includes('ECONNREFUSED')) {
console.error('Connection refused. Ensure the LDAP server is running and accessible.');
}
process.exit(1);
} finally {
if (client.connected) {
await client.unbind().catch(e => console.error('Error during final unbind:', e.message));
}
}
}
connectAndSearch();