Hi I'm new to openldap and nodejs. I'm trying to create openldap authentication and use a simple node app to test that authentication against the local ldap server.
My understanding is that I can create the ldap server and add all the users in Apache Directory Studio. Then write a simple node app with the same configuration as the ldap server. Using postman to send the authentication requests, and I should be able to get the authorized results. Pls correct me if I'm wrong.
Below are the steps I took:
I used Apache Directory Studio to set up a ldap server locally.
Then I tried to set up a simple nodejs app (code is shown below).
When I used Postman to send an authentication request with some user that I set up previously using Apache Directory Studio, but I kept getting error Unauthorized
.
I believe I can hit the node app with my postman calls, because I am able to get that "Unauthorized" response with a username and password that exists in Apache Studio. But the node app is not working with / hooked up with the Ldap server set up by Apache Directory Studio, because I can change the server fields in the node code to completely different from the ldap server but still able to get the unauthorized response in postman. I probably don't have a full understanding of the ldap servers and maybe the node app and the ldap server are completely separate as is? Or this should work it's just something wrong with my code?
Below is my most recent code:
var express = require('express'),
passport = require('passport'),
bodyParser = require('body-parser'),
LdapStrategy = require('passport-ldapauth'),
basicAuth = require('basic-auth')
var OPTS = {
server: {
url: 'ldap://localhost:389',
bindDN: 'cn=admin,ou=users,dc=contoso,dc=com',
bindCredentials: 'P@ss1W0Rd!',
searchBase: 'ou=users,dc=contoso,dc=com',
searchFilter: '(uid={{Username}})'
},
credentialsLookup: basicAuth
// ,
// usernameField: user,
// passwordField: pass
};
var app = express();
passport.use(new LdapStrategy(OPTS));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(passport.initialize());
app.post('/login', passport.authenticate('ldapauth', {session: false}), function(req, res) {
res.send({status: 'ok'});
});
app.listen(8080);
Here's the user I have been trying to authenticate:
dn: cn=Aaron Painter,ou=users,dc=contoso,dc=com
objectClass: top
objectClass: posixAccount
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
cn: Aaron Painter
gidNumber: 70051
homeDirectory: /home/aaronp
sn: Painter
uid: aaronp
uidNumber: 70050
displayName: Aaron Painter
givenName: Aaron
mail: aaronp@contoso.com
manager: cn=Christine Koch,ou=users,dc=contoso,dc=com
telephoneNumber: (212) 555-8335
title: Strategy Consulting Manager
userPassword: AAA
Here's the postman call I used: postman call
Here's the log shown in the server:
contosoOpenLdap | 5d9b8107 conn=1062 fd=19 ACCEPT from IP=172.17.0.1:47712 (IP=0.0.0.0:389)
contosoOpenLdap | 5d9b8107 conn=1063 fd=20 ACCEPT from IP=172.17.0.1:47714 (IP=0.0.0.0:389)
contosoOpenLdap | 5d9b8107 conn=1063 op=0 BIND dn="cn=admin,ou=users,dc=contoso,dc=com" method=128
contosoOpenLdap | 5d9b8107 conn=1063 op=0 RESULT tag=97 err=49 text=
contosoOpenLdap | 5d9b8107 conn=1063 op=1 UNBIND
contosoOpenLdap | 5d9b8107 conn=1063 fd=20 closed
The error code 49 means that there's an incorrect DN or password. But the config seems correct to me.
Pls help thanks.
There are 3 things to fix (provided that your credentials are correct) :
searchBase
(not a rdn)credentialsLookup
, either you set usernameField
and passwordField
. For example, keeping the same username attribute and search base with the proper syntax :
searchBase: 'ou=users,dc=contoso,dc=com',
searchFilter: '(cn={{Username}})'
This should work for authenticating users with the following dn pattern :
cn=<username>,<searchBase> => cn=foo,ou=users,dc=contoso,dc=com
The above is based on your previous config, but one still can't guess what a user dn looks like in your directory without further details. Simply note that manager account (bindDN) and regular users usually don't share the same dn pattern/structure, and the username attribute cn
in your filter may be wrong.
That said, it's pretty obvious to determine the dn structure given a user entry dn string, for example if username attribute is uid
and user base is located under ou=people
:
dn: uid=foo,ou=people,dc=contoso,dc=com
=> searchBase: 'ou=people,dc=contoso,dc=com'
=> searchFilter: '(uid={{Username}})'
Now if you decide to pass credentials via the request body, usernameField
and passwordField
should be set according to, eg. with the following body { "user": "test", "pass": "test" }
, you would set :
//credentialsLookup: basicAuth,
usernameField: user,
passwordField: pass
But if you prefer to set credentials via an authorization header, then you use credentialsLookup
with the desired auth type, and set it in the Authorization tab in Postman.
Note that if you set both credentialsLookup
and the other two fields, then credentialsLookup wins even if the lookup fails (no fallback possible).