import { Client } from "ldapts"; import { IAuthenticationHandler } from "./index.js"; import { TotpChecker } from "./totpChecker.js"; export interface LdapAuthenticationConfiguration { ldapUrl: string; bindDnField: string; bindBase: string; usernameField: string; totpField?: string|null; } interface AccountInformations { username: string; totp: string|null; } export class LdapAuthenticationHandler implements IAuthenticationHandler { private configuration: LdapAuthenticationConfiguration; public constructor(configuration: LdapAuthenticationConfiguration) { this.configuration = configuration; } private async tryBind(username: string, password: string): Promise { if (!username || !password) return null; const client = new Client({ url: this.configuration.ldapUrl, timeout: 0, connectTimeout: 0, tlsOptions: { minVersion: 'TLSv1.2', }, strictDN: true, }); const bindDn = `${this.configuration.bindDnField}=${username},${this.configuration.bindBase}`; let totp: string|null = null; try { await client.bind(bindDn, password); if (this.configuration.totpField) { const data = await client.search(bindDn); let totpData = data.searchEntries[0]?.[this.configuration.totpField]; if (typeof totpData === "string") totp = totpData; if (Array.isArray(totpData)) totp = totpData.join(""); else totp = totpData.toString("utf8"); } } catch (ex) { console.error(ex); return null; } finally { client.unbind(); } return { username: username, totp: totp }; } public async tryLogin(username: string, password: string, totp?: string): Promise { const account = await this.tryBind(username, password); if (!account) return null; return TotpChecker.ValidateTotp(account.totp, totp); } public async needTotp(username: string, password: string): Promise { const account = await this.tryBind(username, password); if (!account) return null; return !!account.totp; } }