├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── example.js ├── lib └── passport-ldap │ ├── index.js │ └── strategy.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.md 2 | .DS_Store 3 | .git* 4 | Makefile 5 | docs/ 6 | examples/ 7 | support/ 8 | test/ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: "node_js" 2 | node_js: 3 | - 0.8 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2011 Paul Dixon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Passport-LDAP 2 | 3 | [Passport](http://passportjs.org/) strategy for authenticating against an OpenLDAP 4 | server. 5 | 6 | This module lets you authenticate against an OpenLDAP server in your Node.js applications. 7 | By plugging into Passport, LDAP authentication can be easily and 8 | unobtrusively integrated into any application or framework that supports 9 | [Connect](http://www.senchalabs.org/connect/)-style middleware, including 10 | [Express](http://expressjs.com/). 11 | 12 | ## Install 13 | 14 | $ npm install passport-ldap 15 | 16 | ## Usage 17 | 18 | #### Configure Strategy 19 | 20 | The LDAP authentication strategy authenticates requests by delegating to the 21 | given ldap server using the openldap protocol. 22 | 23 | The strategy requires a `verify` callback which accepts a user `profile` entry 24 | from the directory, and then calls the `done` callback supplying a `user`. 25 | 26 | ```javascript 27 | passport.use(new LDAPStrategy({ 28 | server: { 29 | url: 'ldap://0.0.0.0:1389' 30 | }, 31 | base: 'cn=users,dc=example,dc=local', 32 | search: { 33 | filter: '(&(l=Seattle)(email=*@foo.com))', 34 | } 35 | }, 36 | function(profile, done) { 37 | return done(null, JSON.parse(profile)); 38 | } 39 | )); 40 | ``` 41 | #### Authenticate Requests 42 | 43 | Use `passport.authenticate()`, specifying the `'ldap'` strategy, to 44 | authenticate requests. 45 | 46 | For example, as route middleware in an [Express](http://expressjs.com/) 47 | application: 48 | ```javascript 49 | app.get('/auth/login', 50 | passport.authenticate('facebook')); 51 | 52 | app.post('/auth/ldap', 53 | passport.authenticate('ldap', { 54 | successRedirect: '/', 55 | failureRedirect: '/auth/login/' 56 | }) 57 | ); 58 | ``` 59 | #### Profile Fields 60 | 61 | | Option | Type | Default | Description | 62 | | ------------- | ------------- | ------------- | ------------- | 63 | | `server` | `Object` | `{url:''}` | Set the server URL in the format of `{url:'url.com:port'}` | 64 | | `usernameField` | `String` | `'user'` | Set the field to use for the `username` from the request sent | 65 | | `passwordField` | `String` | `'pwd'` | Set the field to use for the `password` from the request sent | 66 | | `base` | `String|Array` | `''` | Base DN to search against | 67 | | `search` | `Object` | `{filter:''}` | Object containing search options | 68 | | `authOnly` | `Boolean` | `false` | Whether to only get a successfull authentication with the server without returning the LDAP user | 69 | | `authMode` | `Number` | `1` | Used to differentiate between a Windows `0` or Unix LDAP server `1` | 70 | | `uidTag` | `String` | `uid` | Linux OpenLDAP `uid`, Sun Solaris `cn` | 71 | | `debug` | `Boolean` | `false` | Enable/disable debug messages | 72 | 73 | ## Examples 74 | 75 | For a complete working example refer to the [passport example](/example.js). 76 | 77 | ## Tests 78 | 79 | $ npm install --dev 80 | $ make test 81 | 82 | [![Build Status](https://secure.travis-ci.org/mintbridge/passport-ldap.png)](http://travis-ci.org/mintbridge/mintbridge/passport-ldap) 83 | 84 | ## Credits 85 | 86 | - [Paul Dixon](http://github.com/mintbridge) 87 | 88 | ## License 89 | 90 | [The MIT License](http://opensource.org/licenses/MIT) 91 | 92 | Copyright (c) 2011-2013 Paul Dixon <[http://www.mintbridge.co.uk/](http://www.mintbridge.co.uk)> 93 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var session = require('express-session'); 3 | var LDAPStrategy = require('passport-ldap').Strategy; 4 | 5 | // Windows LDAP 6 | var ldapConfig = { 7 | server: { 8 | url: 'ldap://some.server.url.com:9999' 9 | }, 10 | authMode: 0, 11 | debug: false, 12 | usernameField: 'username', 13 | passwordField: 'password', 14 | base: ['dc=ad','dc=sm','dc=else'], 15 | search: { 16 | filter: '(sAMAccountName=$uid$)', 17 | scope: 'sub', 18 | attributes: ['list','of','user','attributes','you','want','returned'], 19 | sizeLimit: 1 20 | }, 21 | searchAttributes: ['displayName'] 22 | }; 23 | 24 | module.exports = function(app,passport){ 25 | app.use(session({ 26 | secret: 'anawesomesecret' 27 | })); 28 | 29 | passport.use(new LDAPStrategy( 30 | ldapConfig, 31 | function(profile, done) { 32 | return done(null, profile); 33 | } 34 | )); 35 | 36 | passport.serializeUser(function(user,done){ 37 | done(null,user); 38 | }); 39 | 40 | passport.deserializeUser(function(user,done){ 41 | done(null,user); 42 | }); 43 | 44 | app.use(passport.initialize()); 45 | app.use(passport.session()); 46 | }; 47 | -------------------------------------------------------------------------------- /lib/passport-ldap/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var Strategy = require('./strategy'); 5 | 6 | 7 | /** 8 | * Framework version. 9 | */ 10 | require('pkginfo')(module, 'version'); 11 | 12 | /** 13 | * Expose constructors. 14 | */ 15 | exports.Strategy = Strategy; 16 | -------------------------------------------------------------------------------- /lib/passport-ldap/strategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var util = require('util'); 5 | var ldap = require('ldapjs'); 6 | var passport = require('passport'); 7 | 8 | /** 9 | * `Strategy` constructor. 10 | * 11 | * An LDAP authentication strategy authenticates requests by delegating to the 12 | * given ldap server using the openldap protocol. 13 | * 14 | * Applications must supply a `verify` callback which accepts a user `profile` entry 15 | * from the directory, and then calls the `done` callback supplying a `user`, which 16 | * should be set to `false` if the credentials are not valid. If an exception occured, 17 | * `err` should be set. 18 | * 19 | * Options: 20 | * - `server` ldap server connection options - http://ldapjs.org/client.html#create-a-client 21 | * - `base` the base DN to search against 22 | * - `search` an object of containing search options - http://ldapjs.org/client.html#search 23 | * 24 | * Examples: 25 | * 26 | * passport.use(new LDAPStrategy({ 27 | * server: { 28 | * url: 'ldap://0.0.0.0:1389' 29 | * }, 30 | * base: 'cn=users,dc=example,dc=local', 31 | * search: { 32 | * filter: '(&(l=Seattle)(email=*@foo.com))', 33 | * } 34 | * }, 35 | * function(profile, done) { 36 | * return done(null, profile); 37 | * } 38 | * )); 39 | * 40 | * @param {Object} options 41 | * @param {Function} verify 42 | * @api public 43 | */ 44 | function Strategy(options, verify) { 45 | if (typeof options == 'function') { 46 | verify = options; 47 | options = { 48 | server: { 49 | url : '' 50 | }, 51 | usernameField: 'user', 52 | passwordField: 'pwd', 53 | base: '', 54 | search: { 55 | filter: '' 56 | }, 57 | authOnly: false, 58 | authMode: 1, // 0 win, 1 Unix (linux, Solaris, ...) 59 | uidTag: 'uid', // Linux OpenLDAP 'uid', Sun Solaris 'cn' 60 | debug: false 61 | }; 62 | } 63 | if (!verify) throw new Error('LDAP authentication strategy requires a verify function'); 64 | 65 | passport.Strategy.call(this); 66 | 67 | this.name = 'ldap'; 68 | this._verify = verify; 69 | this._options = options; 70 | } 71 | 72 | /** 73 | * Inherit from `passport.Strategy`. 74 | */ 75 | util.inherits(Strategy, passport.Strategy); 76 | 77 | /** 78 | * Authenticate request by binding to LDAP server, and then searching for the user entry. 79 | * 80 | * Command line LDAP bind and search examples: 81 | * - Windows with Active Directory: ldapsearch -H ldap://192.168.1.17:389 -D XXX -w YYY -b dc=example,dc=local objectclass=* 82 | * - Linux/Sun Solaris with OpenLDAP: ldapsearch -H ldap://192.168.1.16:389 -D cn=XXX,dc=example,dc=local -w YYY -b dc=example,dc=local objectclass=* 83 | * 84 | * @param {Object} req 85 | * @param {Object} options 86 | * @api protected 87 | */ 88 | Strategy.prototype.authenticate = function(req, options) { 89 | var self = this; 90 | // Create the client on every auth attempt the LDAP server can close the connection 91 | var Client = ldap.createClient(self._options.server); 92 | 93 | if (!req.body || !req.body[self._options.usernameField] || !req.body[self._options.passwordField]) { 94 | return self.fail(401); 95 | } 96 | 97 | var username = req.body[self._options.usernameField]; 98 | 99 | if (self._options.authMode === 1) { 100 | var base = self._options.base; 101 | if(typeof base !== 'string'){ 102 | base = base.join(','); 103 | } 104 | username = self._options.uidTag + '=' + username + ',' + base; 105 | } 106 | 107 | Client.bind(username, req.body[self._options.passwordField], function (err) { 108 | if (err) { 109 | if (self._options.debug) console.log('(EE) [ldapjs] LDAP error:', err.stack); 110 | return self.fail(403); 111 | } 112 | 113 | if (self._options.authOnly) { 114 | if (self._options.debug) console.log('(II) [ldapjs] auth success:', username); 115 | self.success({ 116 | uid: username 117 | }); 118 | } else { 119 | var dn = username; 120 | if (self._options.authMode !== 1) { 121 | // Add the dc from the username if not already in the configuration 122 | if(typeof self._options.base !== 'string'){ 123 | var nameSplit = username.split('\\'); 124 | var name = nameSplit[1]; 125 | var dc = 'dc=' + nameSplit[0].toLowerCase(); 126 | 127 | dn = self._options.base.slice(); 128 | if (self._options.base.indexOf(dc) === -1) { 129 | dn.splice(0, 0, dc); 130 | } 131 | dn = dn.join(','); 132 | } else { 133 | dn = self._options.base; 134 | } 135 | } 136 | 137 | // Create copy of the search object so we don't overwrite it 138 | var search = Object.create(self._options.search); 139 | 140 | // Replace placeholder name 141 | search.filter = search.filter.replace(/\$uid\$/, name); 142 | Client.search(dn, search, function (err, res) { 143 | if (err) { 144 | if (self._options.debug) console.log('(EE) [ldapjs] LDAP error:', err.stack); 145 | return self.fail(403); 146 | } 147 | 148 | res.on('searchEntry', function(entry) { 149 | var profile = entry.object; 150 | 151 | self._verify(profile, function(err, user) { 152 | if (err) { 153 | if (self._options.debug) console.log('(EE) [ldapjs] LDAP error:', err.stack); 154 | return self.error(err); 155 | } 156 | if (!user) { 157 | if (self._options.debug) console.log('(EE) [ldapjs] LDAP user error:', self._challenge()); 158 | return self.fail(self._challenge()); 159 | } 160 | if (self._options.debug) console.log('(II) [ldapjs] auth success:', user); 161 | self.success(user); 162 | }); 163 | }); 164 | 165 | res.on('error', function(err) { 166 | if (self._options.debug) console.log('(EE) [ldapjs] Network error:', err.stack); 167 | self.error(err); 168 | }); 169 | 170 | res.on('end', function(result) { 171 | if (result.status !== 0) { 172 | if (self._options.debug) console.log('(EE) [ldapjs] Result not OK:', result); 173 | self.fail(result.status); 174 | } 175 | }); 176 | }); 177 | } 178 | 179 | }); 180 | }; 181 | 182 | /** 183 | * Expose `Strategy`. 184 | */ 185 | module.exports = Strategy; 186 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-ldap", 3 | "version": "0.0.3", 4 | "description": "LDAP authentication strategy for Passport.", 5 | "author": { 6 | "name": "Paul Dixon", 7 | "email": "paul.dixon@mintbridge.co.uk", 8 | "url": "http://www.mintbridge.co.uk/" 9 | }, 10 | "contributors": [ 11 | { 12 | "name":"Matteo Brunati" 13 | }, 14 | { 15 | "name":"Jongwook Park", 16 | "email":"niceilm@naver.com" 17 | }, 18 | { 19 | "name":"Paul Dixon", 20 | "email":"paul.dixon@mintbridge.co.uk" 21 | }, 22 | { 23 | "name": "Enzo Martin", 24 | "email": "enzo.martin@dice.se" 25 | } 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/mintbridge/passport-ldap.git" 30 | }, 31 | "bugs": { 32 | "url": "http://github.com/mintbridge/passport-ldap/issues" 33 | }, 34 | "main": "./lib/passport-ldap", 35 | "dependencies": { 36 | "pkginfo": "0.2.x", 37 | "ldapjs": "0.6.3" 38 | }, 39 | "devDependencies": { 40 | "vows": "0.6.x" 41 | }, 42 | "scripts": { 43 | "test": "NODE_PATH=lib node_modules/.bin/vows test/*-test.js" 44 | }, 45 | "engines": { 46 | "node": ">= 0.4.0" 47 | }, 48 | "licenses": [ 49 | { 50 | "type": "MIT", 51 | "url": "http://www.opensource.org/licenses/MIT" 52 | } 53 | ], 54 | "keywords": [ 55 | "passport", 56 | "ldap", 57 | "auth", 58 | "authn", 59 | "authentication", 60 | "identity" 61 | ] 62 | } 63 | --------------------------------------------------------------------------------