This page implements the two group-membership patterns described conceptually in Users and groups: listing a user's groups, and checking membership in one specific group.
memberOfIf the directory maintains a reverse memberOf attribute (Active Directory does natively; OpenLDAP needs the memberof overlay), this is a single search:
async function getUserGroups(client: ldap.Client, userDn: string): Promise<string[]> {
return new Promise((resolve, reject) => {
const opts: ldap.SearchOptions = {
filter: "(objectClass=*)",
scope: "base",
attributes: ["memberOf"],
};
client.search(userDn, opts, (err, res) => {
if (err) return reject(err);
let groups: string[] = [];
res.on("searchEntry", (entry) => {
const value = entry.pojo.attributes.find((a) => a.type === "memberOf")?.values;
groups = value ?? [];
});
res.on("error", reject);
res.on("end", () => resolve(groups));
});
});
}
The result is an array of group DNs, not group names — if you need display names, resolve each DN with a follow-up lookup, or search groups directly (below) and read their cn.
memberOfIf memberOf isn't available, search the group instead, filtering on member:
async function isMember(
client: ldap.Client,
groupDn: string,
userDn: string,
): Promise<boolean> {
return new Promise((resolve, reject) => {
const opts: ldap.SearchOptions = {
filter: `(member=${escapeFilterValue(userDn)})`,
scope: "base",
attributes: ["dn"],
};
client.search(groupDn, opts, (err, res) => {
if (err) return reject(err);
let found = false;
res.on("searchEntry", () => (found = true));
res.on("error", reject);
res.on("end", () => resolve(found));
});
});
}
Because a DN can contain characters like commas and +, always escape it the same way you would any other filter value — see LDAP filters.
Note
Both patterns assume you already have the user's or group's DN. If you're starting from just a username, combine this with the search step from Authentication with ldapjs.
Active Directory resolves nested group membership into memberOf automatically (up to its recursion limit), so a direct memberOf read already reflects indirect membership. Directories without that feature require walking group membership recursively yourself, which is significantly more expensive and usually only necessary for authorization systems with deep role hierarchies.
If your target directory is Active Directory specifically, continue to Active Directory integration in Node.js for AD-specific attributes and quirks.