Rafay Zafer
Rafay Zafer

Reputation: 11

PassportJS Error: No other operations may be performed on the connection while a bind is outstanding intermittently

TLDR: Is there a race condition issue with passportjs or passport-ldapauth?

I am using the koa-passport library with the passport-ldapauth strategy in a nodejs application intended to authenticate a user against AD (Active Directory). Wow, that was a mouthful.

Here is the error I am getting back from passport.authenticate which I'm assuming is coming back from LDAP:

BusyError: 00002024: LdapErr: DSID-0C060810, comment: No other operations may be performed on the connection while a bind is outstanding.

The problem here is obvious, there is an outstanding bind, and it must be closed before I can make another bind to authenticate the next user. The solution however is not, it may either lie with LDAP or it may lie with passportjs. I'm here in hopes to find a solution for the latter. (Going to explore config options for LDAP while waiting for a response on this one #multiprocessing)

Here is my code:

import passport from 'koa-passport';
import LdapStrategy from 'passport-ldapauth';
import { readFileSync } from 'fs';
const ldapCert = readFileSync(process.env.LDAP_CERT, 'utf8');

const ldapConfig = {
  server: {
    url: process.env.LDAP_URL,
    bindDN: process.env.LDAP_BINDDN,
    bindCredentials: process.env.LDAP_PASSWORD,
    searchBase: process.env.LDAP_SEARCH_BASE,
    searchFilter: process.env.LDAP_SEARCH_FILTER,
    searchAttributes: ['sAMAccountName'],
    tlsOptions: {
      ca: [ldapCert]
    }
  }
};

module.exports = async (ctx, next) => {
  passport.initialize();
  passport.use(new LdapStrategy(ldapConfig));
  await passport.authenticate('ldapauth', { session: false }, async (err, user, info) => {
    if (err) {
      console.log('Invalid Authentication Error');
      ctx.throw('INVALID_AUTHENTICATION');
    } else if (!user) {
      console.log('Invalid username or password Error');
      ctx.throw('INVALID_USERNAME_PASSWORD');
    } else {
      await next(); // continue to authorization flow
    }
  })(ctx, next);

Before we get started, know that all the ldapConfigs remain the same throughout the life of the application, so that means I am using the same BINDDN and PASSWORD for every lookup.

So as stated in the title, this error happens intermittently. So the code itself works in general, and I'm able to authenticate users about 95% of the time, and if it ever throws the INVALID_AUTHENTICATION error when the password was correct, that is when I'm getting the the BusyError in the logs.

This problem is more prominent and easier to reproduce when I type in a bogus username/password, ideally I should be prompted with the INVALID_USERNAME_PASSWORD error, which I am about 75% of the time. The other 25% I get INVALID_AUTHENTICATION.

I've even tried to reproduce it using ldapsearch command tool, paired with tmux. I ran a call in ~20 panes simultaneously using the same binddn and they all came back just fine (should I try to run it with more? 100? 1000?). This is what led me to believe the issues was not with LDAP or AD, but more so passportjs.

I concluded with maybe there's a race condition issue with passportJS, but I couldn't find any literature on the interwebs. Has anyone ever encountered something like this? I believe that maybe the bind isn't closing because sometimes passport.authenticate might return before the callback is called? Is that even possible? Does it have something to do with how I coded it with async/await?

My fallback might be to ditch passportjs entirely and just try with ldapjs. Any thoughts, comments, suggestions, discussions will be appreciated

Here is the full stack trace if needed:

BusyError: 00002024: LdapErr: DSID-0C060810, comment: No other operations may be performed on the connection while a bind is outstanding., data 0, v3839
    at messageCallback (/app/node_modules/ldapjs/lib/client/client.js:1419:45)
    at Parser.onMessage (/app/node_modules/ldapjs/lib/client/client.js:1089:14)
    at emitOne (events.js:116:13)
    at Parser.emit (events.js:211:7)
    at Parser.write (/app/node_modules/ldapjs/lib/messages/parser.js:111:8)
    at TLSSocket.onData (/app/node_modules/ldapjs/lib/client/client.js:1076:22)
    at emitOne (events.js:116:13)
    at TLSSocket.emit (events.js:211:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at TLSSocket.Readable.push (_stream_readable.js:208:10)
    at TLSWrap.onread (net.js:597:20)
InternalServerError: INVALID_AUTHENTICATION
    at Object.throw (/app/node_modules/koa/lib/context.js:97:11)

Upvotes: 1

Views: 627

Answers (0)

Related Questions