0

I am using the npm package activedirectory to connect to my company's domain controller for the purpose of authenticating users on an internal website. The plan is for users to enter their windows domain credentials into the website's login page (written with React). The website will forward the credentials to a backend Express server that looks like this:

const express = require('express');
const bodyParser = require('body-parser');
const ActiveDirectory = require('activedirectory');

const app = express();
app.use(bodyParser.urlencoded({
    extended: false,
}));

app.get('/ldap', (request, response) => {
    // set http header for json
    response.setHeader('ContentType', 'application/json');

    // get the username and password from the query
    const username = request.query.username + '@internal.mycompany.com';
    const password = request.query.password;
    console.log(username, password);

    // create a new active directory connection
    let activeDirectory = new ActiveDirectory({
        url: 'LDAP://internal.mycompany.com',
        baseDN: 'DC=mycompany,DC=com',
    });

    // attempt authentication
    activeDirectory.authenticate(username, password, (error, authenticated) => {
        if(error) {
            console.log('ERROR: ' + JSON.stringify(error));
        }
        else {
            console.log('Authentication successful');
        }

        // respond
        response.send(JSON.stringify({
            error: error,
            authenticated: authenticated,
        }));
    });

});

app.listen(3001, () => {
    console.log('Express server running on port 3001.');
});

The React frontend executes this when the 'Log In' button is clicked:

login() {
    console.log('authenticating with username: ', this.state.username, ' and password: ', this.state.password);
    fetch(`/ldap/?username=${encodeURIComponent(this.state.username)}&password=${encodeURIComponent(this.state.password)}`).then((response) => {
        return response.json();
    }).then((response) => {
        console.log(response);
        this.props.setLoggedIn(response.authenticated);
        this.setState({
            loginFailed: !response.authenticated,
            redirectToHome: response.authenticated,
        });
    });
}

It calls fetch to ask the Express server to authenticate the credentials, and sets some global loggedIn state accordingly. It also sets local state to display an error message if the login attempt failed or redirect the user on to the main website homepage if the login attempt was successful.

Leaving the username and password blank yields this response:

{
    error: {
        code: 49,
        description: "The supplied credential is invalid",
        errno: "LDAP_INVALID_CREDENTIALS",
    },
    authenticated: false,
}

Typing in a valid username and password yields this response:

{
    error: null,
    authenticated: true,
}

This is all as expected up to this point.

However, typing in random characters for the username and password yields one of 2 responses. It either authenticates successfully, which shouldn't happen, or it gives this:

{
    error: {
        lde_dn: null,
        lde_message: "80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db1",
    },
    authenticated: false,
}

QUESTION: Why would giving the domain controller garbage credentials cause it to return authenticated: true? And why only sometimes? Some information must be cached or remembered somewhere. I tried restarting the Express server and I tried waiting a day for something to expire.

Note: I was planning to encrypt/scramble the passwords so that they are not being sent in plain text, but if there is an even better way to send them securely, please leave a comment about how to improve this. But for now the main question is about the incorrect Active Directory/LDAP authentication.

nullromo
  • 2,165
  • 2
  • 18
  • 39
  • This would not explain why it's intermittent, but is the guest account enabled on that domain? – Gabriel Luci May 13 '20 at 17:08
  • I'm not sure, and after reading around for ~1hr, I can't find how to check. My guess is that it's not, but I don't know. I did try to query the ActiveDirectory to find a user named `guest` or a group named `Guests`, but those could have been renamed. When querying for those things, however, I was unable to get results because I got an error saying "In order to perform this operation a successful bind must be completed on the connection," even when I did it after a successful authentication. Do you know how the guest account being enabled could be contributing to the issue at hand? – nullromo May 13 '20 at 19:15
  • I remember reading [this](https://stackoverflow.com/a/7336610/1202807). The guest account is just a user account called `Guest` on the domain. It's in the built-in Users container at the root of the domain. The account is disabled by default. – Gabriel Luci May 13 '20 at 21:48
  • Hmm, that is an interesting lead. In my case however, entering a _known_ user with a bad password _still_ authenticates. I will see if I can get my system admin to disable the guest account. – nullromo May 14 '20 at 01:40
  • It's also worth noting that the [activedirectory](https://www.npmjs.com/package/activedirectory) npm package hasn't been touched in years. Someone forked it and is maintaining it under the name [activedirectory2](https://www.npmjs.com/package/activedirectory2). You might be better off using that package. – Gabriel Luci May 14 '20 at 02:06
  • I wasn't aware of that, thanks. I'll look into activedirectory2 and see if that helps the issue. – nullromo May 15 '20 at 06:39

1 Answers1

0

This information: https://www.rfc-editor.org/rfc/rfc4513#section-6.3.1 essentially tells us that getting authenticated: true may not actually mean anything. The solution I came up with is to try and query the Active Directory to see if the user that just authenticated exists and/or try doing an/some operation(s) as the newly-authenticated user. If the operation(s) fail(s), then the login was not valid.

Additionally, there was an error in the above code. baseDN should have been 'DC=internal,DC=mycompany,DC=com'.

Community
  • 1
  • 1
nullromo
  • 2,165
  • 2
  • 18
  • 39
  • See this question as well: https://stackoverflow.com/questions/62310951/intermittent-authentication-against-active-directory-from-node/62600671#62600671 – nullromo Jun 26 '20 at 18:18