Skip to content

Instantly share code, notes, and snippets.

@DavesCodeMusings
Last active August 20, 2022 03:03
Show Gist options
  • Select an option

  • Save DavesCodeMusings/d1014d89f48122d67a3f9d3456f88a1d to your computer and use it in GitHub Desktop.

Select an option

Save DavesCodeMusings/d1014d89f48122d67a3f9d3456f88a1d to your computer and use it in GitHub Desktop.
Example of LDAP user authentication using ldapjs
#!/usr/bin/env node
/**
* Example of LDAP user authentication using ldapjs.
*
* Run like this:
* auth.js ; echo $?
*
* A successful authentication will show output like this along with a return code of 0:
* Connecting to: [ 'ldap://127.0.0.1:389' ]
* TLS started.
* Binding as: uid=search,dc=home
* Searching ou=People,dc=home for: uid=dave
* Rebinding as: uid=dave,ou=People,dc=home
* Authenticated.
* Disconnecting.
*
* Unsuccessful authentication will show error messages and a return code of 255.
*/
import {readFileSync} from 'fs';
import ldapjs from 'ldapjs';
// The following were tested using OpenLDAP and the PosixAccount object class.
// Adjust as needed for your setup.
const ldapURL = [ 'ldap://127.0.0.1:389' ]; // Array can contain additional LDAP servers to try.
const bindDN = 'uid=search,dc=home';
const bindPassword = 'password';
const baseDN = 'ou=People,dc=home';
const usernameAttribute = 'uid';
const filter = '(objectClass=posixAccount)';
// In a real application, the user credentials would come from a login form.
var username = 'dave';
var password = 'password';
process.exitCode = -1; // Return this value to the shell if not successful.
// Most of the rest is pieced together from the documentation at: http://ldapjs.org/client.html
// And a helpful example from 'kohleman' at: https://github.com/ldapjs/node-ldapjs/issues/326
console.debug('Connecting to server:', ldapURL);
const client = ldapjs.createClient({
url: ldapURL
});
client.on('error', (err) => {
console.error('LDAP error:', err.message);
});
client.on('connect', () => {
const opts = {
ca: [fs.readFileSync('/etc/ssl/certs/ca-certificates.crt', 'utf-8')],
rejectUnauthorized: false // Helps with self-signed certs.
};
client.starttls(opts, client.controls, (err) => {
if (err) {
console.error(err.message);
client.unbind(() => {
console.log('Disconnecting.');
});
}
else {
console.debug('TLS started.');
console.debug('Binding as:', bindDN);
client.bind(bindDN, bindPassword, (err) => {
if (err) {
console.error(err.message);
client.unbind(() => {
console.log('Disconnecting.');
});
}
else {
const opts = {
attributes: usernameAttribute,
filter: `&${filter}(${usernameAttribute}=${username})`,
scope: 'sub'
};
console.debug('Searching', baseDN, 'for:', `${usernameAttribute}=${username}`);
client.search(`${baseDN}`, opts, (err, res) => {
let numMatch = 0;
let userDN = '';
if (err) {
console.error(err.message);
client.unbind(() => {
console.log('Disconnecting.');
});
}
res.on('error', (err) => {
console.error('Error:', err.message);
client.unbind(() => {
console.log('Disconnecting.');
});
});
res.on('searchEntry', (entry) => { // There was a match.
numMatch++;
userDN = entry.object.dn;
});
res.on('end', () => {
if (numMatch == 1) {
console.debug('Rebinding as:', userDN);
client.bind(userDN, password, (err) => {
if (err) {
console.error('Error:', err.message);
}
else {
process.exitCode = 0;
console.log('Authenticated.');
}
});
}
else if (numMatch > 1) {
console.error('Multiple matches returned.');
}
else {
console.error('Not found.');
}
client.unbind(() => {
console.log('Disconnecting.');
});
});
});
}
});
}
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment