Basic Active Directory wrapper for ldapjs
npm install node-ad-toolsAccount is locked out
test and the domain of test.local, but the user has a UPN of test@test.com.
test\test
test@test.com
CN=Test,OU=Users,DC=test,DC=local
test@test.local we would need to convert the string to test\test or we would need to provide a customSearch on the loginUser method. Originally this was being added to v1.2.0 as an auto-fallback if no user was located with UPN, however due to unknown possible security issues and lack of testing time I decided it'd be safe to leave this process up to the dev and not include the auto-fallback process in v1.2.0.
javascript
const username = 'test@test.local';
const sam = username.split('@')[0];
const customSearch = { filter: (sAMAccountName=${sam}) }
myAD.loginUser(username,'Welcome1',null , customSearch)
`
API
Install
yarn add node-ad-tools
$3
The active directory class requires a basic configuration object that will inform ldapjs of the binding and searching parameters. This is configured once by creating a new ActiveDirectory object, if you need to change these settings dynamically you can construct the object right before performing the auth.
`javascript
{
url: 'ldap://192.168.1.1',
base: 'dc=domain,dc=local',
searchOptions: {scope: 'sub'}, // Optional
idleTimeout: 3000, // Optional
tlsOptions: { rejectUnauthorized: false } // Optional
}
`
$3
`javascript
const { ActiveDirectory } = require('node-ad-tools');
const myADConfig = {
url: 'ldap://192.168.1.1', // You can use DNS as well, like domain.local
base: 'dc=domain,dc=local'
}
const myAD = new ActiveDirectory(myADConfig);
myAD.loginUser('test@domain.local','password')
.then(res => {
// If it failed to auth user find out why
if(!res.success) {
console.log(res.message);
return;
}
const user = ActiveDirectory.createUserObj(res.entry);
console.log(user);
})
.catch(err => console.error(err))
`
Both the class configuration and the methods that interact with Active Directory accept a base. The class one will be the default used if the base is not passed into specific methods. Here is an example of a base:
`javascript
// This searches only in the Users OU inside example.local
myAD.loginUser('test@domain.local','password','cn=Users,dc=example,dc=local')
`
#### Constructor Config Options
| Key | Type | Required | Description |
| --- | ---- | -------- | ----------- |
| url | String | Required | The url to the AD server, should start with ldap:// or ldaps:// |
| base | String | Required | AD base, example.local would be dc=example, dc=local|
| searchOptions | Object | Optional | ldapjs searchOptions, defaults to scope: 'sub' |
| idleTimeout | Number | Optional | How long to wait for response from AD before timing out |
| tlsOptions | Object | Optional | Node TLS options used when connecting using TLS. See Node TLS API for details about options. |
Methods
$3
This function takes a username and password and will return a Promise. The promise will only reject client connection issues, invalid authentication will still resolve the promise. This was done to make it easier to provide a different error or to try a 2ndry auth source easily. The success key is on all types of responses and should be used to verify if user was logged in. If success is false there will be 2 additional keys, message and error.
The param customSearch was added in v1.2.0 and allows you to override the search for the user object if the default process is not sufficient. To view all available options please look at ldapjs search options. For example you can modify the search by passing in a custom filter key.
If the bind is successful, but the method is unable to locate an account the res.entry will be undefined. This means the credentials passed are valid credentials, however the filter / AD was unable to locate a matching account. You will likely need to provide a customSearch in this case.
`javascript
myAD.loginUser('test@domain.local','password')
.then(res => {
// If it failed to auth user find out why
if(!res.success) {
console.log(res.message);
return;
}
const user = ActiveDirectory.createUserObj(res.entry);
console.log(user);
})
.catch(err => console.error(err))
`
Both resolve & reject will be in the following format
| Key | Returned | Type | Description |
| --- | -------- | ---- | ----------- |
| success | Always | boolean | Indicates if the login succeeded |
| entry | Situational | Object|Undefined | Entry is the ldapjs entry response |
| message | Situational | String | User friendly message from resolveBindError, only on success: false |
| error | Situational | String | The original error generated, only on success: false |
$3
Look-up all the groups in active directory that the user can read, which is based on read permission configuration in active directory. All groups are returned in array of strings.
The detailed param was added in v1.2.0 and will create group objects for every group returned. These group objects contain additional useful information and in v2.0.0 will be on by default.
This is all groups the user can read, not just groups the user is a member of.
Regular none-detailed groups
`javascript
[
'Domain Users',
'Domain Guests',
'Group 1',
'Group 2'
]
`
Detailed groups
`javascript
[
{
name: 'My Group',
dn: 'CN=My Group,CN=Users,DC=test,DC=local',
guid: 'a4f84d99-e0c8-4e60-87e3-53444fd6fe0a',
description: 'This is my test group!',
created: '2018-07-27T20:44:07.000Z', // JS Date Obj - This is not a string, I showed a string for demonstration
changed: '2019-02-27T16:39:18.000Z' // JS Date Obj
}
]
`
This function takes a username and password and will return a Promise. The promise will only reject client connection issues, invalid authentication will still resolve the promise. This was done to make it easier to provide a different error or to try a 2ndry auth source easily. The success key is on all types of responses and should be used to verify if user was logged in. If success is false there will be 2 additional keys, message and error.
`javascript
myAD.getAllGroups('test@domain.local','password')
.then(res => {
// If it failed to auth user find out why
if(!res.success) {
console.log(res.message);
return;
}
console.log(res.groups);
})
.catch(err => console.error(err))
`
Get all groups in detailed mode and provide no custom base example:
`javascript
myAD.getAllGroups('test@domain.local','password', true)
.then(res => {
// If it failed to auth user find out why
if(!res.success) {
console.log(res.message);
return;
}
console.log(res.groups);
})
.catch(err => console.error(err))
`
Both resolve & reject will be in the following format
| Key | Returned | Type | Description |
| --- | -------- | ---- | ----------- |
| success | Always | boolean | Indicates if the login succeeded |
| groups | Situational | Array | An array of all the groups the user has permissions to read in AD. |
| message | Situational | String | User friendly message from resolveBindError, only on success: false |
| error | Situational | String | The original error generated, only on success: false |
$3
Look-up all the users in active directory that the user can read, which is based on read permission configuration in active directory. All user entry objects are returned in an array.
The formatted param was added in v1.2.0 and if set to true will convert all entries into user objects using the ActiveDirectory.createUserObj() method.
`javascript
{
success: true,
users: [
// This is a valid entry just like login user and can be passed to createUserObj() method.
SearchEntry,
SearchEntry
]
}
`
Example with formatted set to true:
`javascript
{
success: true,
users: [
{
groups: [ 'Staff', 'Users' ],
phone: '',
name: 'First User',
mail: 'firstuser@test.com',
guid: '579de45e-faf5-40f8-8eff-be2d76bd20d9',
dn: 'CN=First User,OU=Users,DC=test,DC=com'
},
{
groups: [ 'Staff', 'Users' ],
phone: '',
name: 'Second User',
mail: 'seconduser@test.com',
guid: '502de45e-faf9-83f8-8eff-be6d76bd20d5',
dn: 'CN=Second User,OU=Users,DC=test,DC=com'
}
]
}
`
This function takes a username and password and will return a Promise. The promise will only reject client connection issues, invalid authentication will still resolve the promise. This was done to make it easier to provide a different error or to try a 2ndry auth source easily. The success key is on all types of responses and should be used to verify if user was logged in. If success is false there will be 2 additional keys, message and error.
`javascript
myAD.getAllUsers('test@domain.local','password','cn=Users,dc=domain,dc=local', true) // Example of only searching Users OU inside the domain
.then(res => {
// If it failed to auth user find out why
if(!res.success) {
console.log(res.message);
return;
}
console.log(res.users);
})
.catch(err => console.error(err))
`
Both resolve & reject will be in the following format
| Key | Returned | Type | Description |
| --- | -------- | ---- | ----------- |
| success | Always | Boolean | Indicates if the login succeeded |
| Users | Situational | Array | An array of all the user entries the user has permissions to read in AD and match the base / scope. |
| message | Situational | String | User friendly message from resolveBindError, only on success: false |
| error | Situational | String | The original error generated, only on success: false |
$3
Takes in the entry returned by ldapjs and creates a standardized user object. If you do not want to store all the users data it is recommended you extract the values you need from this object, because in the future there will likely be many more fields added to this. The first set of fields added were based on immediate needs.
If this does not have all the desired fields please feel free to add more in a PR or you can simply access them on the entry.objects or entry.attributes if you need the buffers.
The user DN was added to the user object in v1.2.0
`javascript
const user = ActiveDirectory.resolveBindError(res.entry)
console.log(user) // {groups: [], phone: '', name: '', mail: '', guid: '', dn: ''}
`
Returns Object
| Returned | Type | Description |
| -------- | ---- | ----------- |
| groups | Array | An array of group name strings. This is the group names only, not the full AD location |
| phone | String | Users phone number |
| name | String | Users full name |
| mail | String | Users email address |
| guid | String | Unique AD key, this should be used to track and or link the user account to your app. |
$3
This function takes in the ldapjs errors and checks if it's due to invalid credentials or if the account is locked out. This does not check if an account is disabled, so it will still return as invalid credentials
`javascript
const message = ActiveDirectory.resolveBindError(res.entry)
// Examples: Account is locked out, Invalid username or password, or Error resolving account.
`
$3
Takes in the entry returned by ldapjs and creates a GUID string. This should be used as your unique ID in your app or somehow used to link to a unique ID in your app. This will not change for the life of the object in AD, so even if the users name or email is changed this will stay the same.
`javascript
const guid = ActiveDirectory.resolveGUID(res.entry)
// Example: 17d4e710-624d-4978-900b-8549cb753699
`
$3
Takes in the entry returned by ldapjs and creates an array of the users groups.
`javascript
const guid = ActiveDirectory.resolveGroups(res.entry)
// Example: ['Group1', 'Group2']
``