26

I am creating a login authentication page, where a user would input there active directory username and password and using NodeJS I would check to see if it's valid, but I keep getting

[Error: LDAP Error Bad search filter]

or

[Error: Search returned != 1 results]

When I'm trying to search for the username and password, my code is below:

I'm using: https://github.com/jeremycx/node-LDAP, let's say that the user entered a username of hhill

    var ldap = require('LDAP');
    var ldapServer = new ldap({ uri: 'ldap://batman.lan', version: 3});

    ldapServer.open(function(error) {
        if(error) {
           throw new Error('Cant not connect');
        } else {
            console.log('---- connected to ldap ----');

            username = '(cn='+username+')';
            ldapServer.findandbind({
                base: 'ou=users,ou=compton,dc=batman,dc=lan',
                filter: username,
                password: password
            }, function(error, data) {
                if(error){
                    console.log(error);
                } else {
                    console.log('---- verified user ----');
                }
            });
        }
    });

Does anyone have any suggestions on what I'm doing wrong?

UPDATE

Here is the solution I came up with if anyone ever needs it, with the help of the answer below

    var username = request.param('username');
    var password = request.param('password');

    var ldap = require('ldapjs');
    ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
    var client = ldap.createClient({
          url: 'ldap://batman.com/cn='+username+', ou=users, ou=compton, dc=batman, dc=com',
          timeout: 5000,
          connectTimeout: 10000
    });
    var opts = {
      filter: '(&(objectclass=user)(samaccountname='+username+'))',
      scope: 'sub',
      attributes: ['objectGUID']
    };

    console.log('--- going to try to connect user ---');

    try {
        client.bind(username, password, function (error) {
            if(error){
                console.log(error.message);
                client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
            } else {
                console.log('connected');
                client.search('ou=users, ou=compton, dc=batman, dc=com', opts, function(error, search) {
                    console.log('Searching.....');

                    search.on('searchEntry', function(entry) {
                        if(entry.object){
                            console.log('entry: %j ' + JSON.stringify(entry.object));
                        }
                    });

                    search.on('error', function(error) {
                        console.error('error: ' + error.message);
                    });

                    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
                });
            }
        });
    } catch(error){
        console.log(error);
        client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
    }
1
  • In the first case, which filter is actually transmitted to the server? In the second case, more than one object matched the search parameters: perhaps it expects just one match. Commented Jul 22, 2013 at 19:01

3 Answers 3

17

In this case, you need ldapClient rather than ldapServer, this is the example code from the official doc:

var ldap = require('ldapjs');

ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;

var client = ldap.createClient({
  url: 'ldap://127.0.0.1/CN=test,OU=Development,DC=Home'
});

var opts = {
  filter: '(objectclass=user)',
  scope: 'sub',
  attributes: ['objectGUID']
};

client.bind('username', 'password', function (err) {
  client.search('CN=test,OU=Development,DC=Home', opts, function (err, search) {
    search.on('searchEntry', function (entry) {
      var user = entry.object;
      console.log(user.objectGUID);
    });
  });
});
Sign up to request clarification or add additional context in comments.

2 Comments

I've tried the above, I get the following error oSuchObjectError: 0000208D: NameErr: DSID-0310020A, problem 2001 (NO_OBJECT), data 0, best match of: 'OU=Users,OU=Newyork,DC=basingloc,DC=lan' It connects, but the search fails. on the line search.on @sza
Just fixed issues I had using this solution. Thank you @zsong!
10

@Sukh Thank you for posting your UPDATE solution; however, there is a problem with the code you posted in your UPDATE. While it works for simple cases, with larger queries, you will find you are unbinding before the results have been output. The solution for me was to move your unbinds into the search.on functions.

Here is an edit of your UPDATE:

var ldap = require('ldapjs');
ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
var client = ldap.createClient({
      url: 'ldap://batman.com/cn='+username+', ou=users, ou=compton, dc=batman, dc=com',
      timeout: 5000,
      connectTimeout: 10000
});
var opts = {
  filter: '(&(objectclass=user)(samaccountname='+username+'))',
  scope: 'sub',
  //attributes: ['objectGUID']
  // This attribute list is what broke your solution
  attributes: ['objectGUID','sAMAccountName','cn','mail','manager','memberOf']
};

console.log('--- going to try to connect user ---');

try {
    client.bind(username, password, function (error) {
        if(error){
            console.log(error.message);
            client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
        } else {
            console.log('connected');
            client.search('ou=users, ou=compton, dc=batman, dc=com', opts, function(error, search) {
                console.log('Searching.....');

                search.on('searchEntry', function(entry) {
                    if(entry.object){
                        console.log('entry: %j ' + JSON.stringify(entry.object));
                    }
                    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
                });

                search.on('error', function(error) {
                    console.error('error: ' + error.message);
                    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
                });

                // don't do this here
                //client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
            });
        }
    });
} catch(error){
    console.log(error);
    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
}

At least this is what I discovered when using your solution with Active Directory searches. memberOf returns A LOT of entries in my use case and the unbinds were being done prematurely, so I was getting the following error:

error: 1__ldap://my.domain.com/,OU=Employees,OU=Accounts,DC=my,DC=domain,DC=com closed
client disconnected

Comments

-2

Suggestions

1.Don't use ldapauth-fork (Huge hanging issue, if we hit multiple requests then after some time library gets unresponsive and doesn't return anything.)

2.Don't use passport-ldapauth (Internally calls ldapauth-fork)

We can use ldapjs, which has easy implementation and is based on event driven approach.

Below nodejs code explain complete solution for ldap auth and search.

JS code

const ldap = require('ldapjs');
let client

// unbind after completion of process
function closeConnection() {
    console.log('closeConnection')
    client.unbind(err => {
        console.log('unbind error', err)
    });
}

function search() {
    const searchOptions = {
        filter: '(uid=yourSearchText)', // search text
        scope: 'sub'
    };
    return new Promise((resolve, reject) => {
        client.search('ou=consultants,' + 'ou="Your OU",ou=yourOu,dc=yourDc,dc=com', searchOptions, (err, res) => {
            res.on('searchEntry', entry => {
                console.log('searchEntry', entry.object);
                resolve(entry.object)
            });
            res.on('searchReference', referral => {
                console.log('referral: ' + referral.uris.join());
                resolve(referral.uris.join())
            });
            res.on('error', err => {
                console.error('search error: ' + err.message);
                reject(err)
            });
            res.on('end', result => {
                console.log('If not found', result);
                reject({ message:'User not found'})
            });
        });
    })
}

function authenticate() {
    const server = 'ldap server ip';
    client = ldap.createClient({
        url: `ldap://${server}`
    });

    return new Promise((resolve, reject) => {
        client.bind('cn=yourcn,dc=yourdc,dc=com', 'sortedSolutions', err => {
            if (err) {
                reject(err)
            }
            resolve('Authenticated successfully')
        });
    })
}

function start(req, res) {
    let searchResponseData
    authenticate()
        .then(authenticateResponse => {
            console.log('authenticateResponse', authenticateResponse)
            return search()
        })
        .then(searchResponse => {
            console.log('searchResponsesearchResponse', searchResponse)
            searchResponseData = searchResponse
            return closeConnection()
        })
        .then(closeConnectionResponse => {
            console.log('ldap connection closed', closeConnectionResponse)
            res.status(200).send(searchResponseData)
        })
        .catch(error => {
            console.log('catch error', error)
            res.status(400).send(error)
        })

}

module.exports.start = start

// We can use same code with no authentication, Just pass '' to bind function client.bind('', '', err => { //same as above })

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.