Интеграция Active Directory / LDAP для корпоративного мобильного приложения
Прямая LDAP-интеграция в мобильном приложении — это почти всегда архитектурная ошибка. Мобильный клиент не должен соединяться с LDAP-сервером напрямую: порты 389/636 закрыты за корпоративным файрволом, LDAP-bind credentials нельзя хранить на устройстве, соединение через мобильный интернет к on-premise AD — нестабильно. Правильная схема: мобильный → backend API → AD/LDAP.
Архитектура интеграции
Backend выступает LDAP-прокси: принимает запросы от мобильного через HTTPS с JWT-авторизацией, обращается к AD/LDAP внутри корпоративной сети, возвращает данные в REST-формате.
Мобильный ──HTTPS/JWT──► Backend API ──LDAP 636──► Active Directory
└──LDAP 389──► OpenLDAP
Для аутентификации через AD на backend используем LDAP-bind:
// Node.js, ldapjs
const client = ldap.createClient({
url: "ldaps://dc01.company.local:636",
tlsOptions: { rejectUnauthorized: true, ca: [fs.readFileSync("ca.crt")] },
});
async function authenticateUser(username, password) {
const userDN = `cn=${username},ou=Users,dc=company,dc=local`;
return new Promise((resolve, reject) => {
client.bind(userDN, password, (err) => {
if (err) {
reject(new InvalidCredentialsError());
} else {
resolve(true);
client.unbind();
}
});
});
}
После успешного bind backend генерирует JWT и возвращает на мобильный. Пароль пользователя никогда не покидает backend.
Получение атрибутов пользователя
AD хранит богатый набор атрибутов: displayName, mail, telephoneNumber, department, manager, memberOf (группы), thumbnailPhoto (аватар). Для корпоративного приложения это ценный источник данных — не нужно дублировать профили пользователей.
async function getUserAttributes(username) {
const base = "ou=Users,dc=company,dc=local";
const opts = {
filter: `(sAMAccountName=${username})`,
scope: "sub",
attributes: [
"displayName",
"mail",
"department",
"manager",
"memberOf",
"thumbnailPhoto",
],
};
return new Promise((resolve, reject) => {
client.search(base, opts, (err, res) => {
let entry = null;
res.on("searchEntry", (e) => (entry = e.object));
res.on("end", () => resolve(entry));
res.on("error", reject);
});
});
}
thumbnailPhoto — это JPEG в base64 прямо в AD. Возвращаем его как base64-строку или сохраняем в object storage и возвращаем URL.
memberOf содержит DN групп: CN=VPN-Users,OU=Groups,DC=company,DC=local. Группы определяют права пользователя в приложении — парсим CN из DN и маппим на application roles.
Работа с оргструктурой
AD содержит иерархию через атрибут manager (DN руководителя) и directReports. Построить дерево оргструктуры через рекурсивные LDAP-запросы — возможно, но медленно при глубоких иерархиях. Лучше: кэшировать структуру на backend с периодическим обновлением (раз в час/сутки), мобильный запрашивает готовый граф.
// Android — отображение оргструктуры
data class OrgNode(
val employeeId: String,
val name: String,
val position: String,
val department: String,
val avatarUrl: String?,
val directReports: List<OrgNode>
)
@Composable
fun OrgChart(rootNode: OrgNode) {
LazyColumn {
item { EmployeeCard(node = rootNode, level = 0) }
items(rootNode.directReports) { report ->
EmployeeCard(node = report, level = 1)
// Рекурсивно для вложенных уровней через expandable state
}
}
}
Поиск сотрудников
Полнотекстовый поиск по AD через LDAP-фильтр:
const filter = `(&(objectClass=person)(|(displayName=*${query}*)(mail=*${query}*)(sAMAccountName=*${query}*)))`;
Производительность: wildcard в начале фильтра (*${query}*) не использует индекс AD, поиск медленный при большой базе. Для приложений с тысячами сотрудников — синхронизируем AD в Elasticsearch или PostgreSQL full-text search, поиск делаем там.
Синхронизация и кэш
AD — источник правды для данных о сотрудниках. Мобильное приложение работает с кэшем backend. Для актуальности: webhook-события через AD Event Log (если AD 2016+) или polling раз в 15-30 минут на изменения через uSNChanged атрибут.
При увольнении сотрудника аккаунт в AD деактивируется. Backend при следующем refresh_token видит failed LDAP lookup и инвалидирует JWT. Мобильный перенаправляется на логин.
Особенности Azure AD (Entra ID)
Если компания использует Azure AD (Microsoft Entra ID) — прямого LDAP нет, только Microsoft Graph API или OIDC. Graph API значительно удобнее: REST, JSON, богатая документация. GET /users/{id}?$select=displayName,mail,department,manager,memberOf возвращает всё то же, что и LDAP, но без ADO-библиотек.
Для гибридной среды (on-premise AD + Azure AD с Azure AD Connect) — данные синхронизируются, можно использовать либо Graph API, либо on-premise LDAP в зависимости от требований безопасности.
Интеграция AD/LDAP (backend-прокси + мобильный клиент + оргструктура + поиск): 3-6 недель. Стоимость рассчитывается индивидуально после анализа инфраструктуры заказчика.







