How to connect to LDAP with NTLM in Node.js

191 views Asked by At

I'm building an app where employees should be able to login with their LDAP credentials through a web form.

I have a problem converting this working Python function, that uses ldap3 library, into Node:

def authenticate_ldap(
        username: str,
        password: str
        ) -> dict[str, str, str]:
    try:
        conn = Connection(
            server='ldapDomain', user=username,
            password=password, authentication=NTLM,
            auto_bind=True
        )
        conn.search(
            'DC=smth,DC=com', f'(sAMAccountName={username})',
            attributes=['department', 'displayName', 'description']
        )
        name = conn.entries[0].displayName
        filial = conn.entries[0].department
        pos = conn.entries[0].description

    except LDAPBindError:
        return False
    data_user = {
        'name': str(name),
        'department': str(filial),
        'position': str(pos)
        }
    return data_user

Unfortunately, noone is familiar with Node in my company and I've been searching and trying for some time without any luck.

I've tried using windows authentication strategy for Passport.js (non-integrated one), but not sure how to pass username and password to the function and when I hardcode them, it still throws 52e error (even with a wrong username).

Update: I was able to connect via Postman using express-ntlm library and had no luck to do the same through a browser form. Tried to send an auth header ('NTLM ' + 'base64encoded credentials'), but it returns 'Not a valid NTLM message:' error.

And just to add some more info: I've played around with the python function and it is necessary to use NTLM, simple bind doesn't work.

1

There are 1 answers

1
ixe013 On

You probably fail the first bind and it goes downhill from there.

I don't know Node much either, but I know a thing or two about LDAP. Here is how you usually go about it.

First, you need in your configuration (along with the hostname of the LDAP server):

  1. The distinguished name of a service account for your application. It should look something like this cn=myapp,ou=users,dc=smth,dc=com
  2. A password for your service account

With that in hand, when you receive a username password combination to validate, you go through the following:

  1. Connect to the LDAP server with your service account, using SIMPLE bind, not NTLM
  2. Search for the user to get their distinguished name (aka dn)
  3. Bind again with the dn of the user and the password you received.

I don't have anything to test, but the code should look like this:

def authenticate_ldap(
        username: str,
        password: str
        ) -> dict[str, str, str]:
    try:
        conn = Connection(
            server='ldapDomain', user=config.app_username,
            password=config.app_password
        )
        conn.search(
            'DC=smth,DC=com', f'(sAMAccountName={username})',
            attributes=['department', 'displayName', 'description']
        )

        user_dn = conn.entries[0].entry_dn 

        conn.unbind() #YMMV, but I prefer to unbind the app

    except LDAPBindError:
        # Save yourself a lot of blind debugging with two try statements
        print("The application cannot connect or something")
        return False

    try:        
        conn = Connection(
            server='ldapDomain', user=user_dn,
            password=password
        )

        # Validate the user's password
        name = conn.entries[0].displayName
        filial = conn.entries[0].department
        pos = conn.entries[0].description

    except LDAPBindError:
        print("The application cannot validate the password somehow")
        return False

    return {
        'name': str(name),
        'department': str(filial),
        'position': str(pos)
        }