Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

AuthHelper for basic authentication added.

  • Loading branch information...
commit 3cbe3a51c0a731fb22451f068d491f1f0799755a 1 parent b756e2b
Gevorg Harutyunyan authored
2  README.md
View
@@ -107,7 +107,7 @@ http.createServer(function(req, res) {
## Configurations
- `authRealm` - Authentication realm.
- - `authHelper` - Function that allows to override standard authentication method by providing custom user loading mechanism. Works only with digest authentication.
+ - `authHelper` - Function that allows to override standard authentication method by providing custom user loading mechanism.
- `authFile` - File where user details are stored in format **{user:pass}** or **{user:passHash}** for basic access and **{user:realm:passHash}** for digest access.
- `authList` - List where user details are stored in format **{user:pass}** or **{user:passHash}** for basic access and **{user:realm:passHash}** for digest access, ignored if `authFile` is specified.
- `authType` - Type of authentication, may be **basic** or **digest**, optional, default is **basic**.
8 examples/example_helper.js
View
@@ -11,17 +11,17 @@ var http = require('http');
/**
* Requesting new authentication instance.
*/
-var digest = auth({
+var basic = auth({
authRealm : "Private area.",
// username is mia, password is supergirl.
authHelper : function(user, callback) {
if ( user === 'mia' ) {
- callback('mia:Private area.:3a556dc7260e8e7f032d247fb668b06b');
+ callback('{SHA}x511ncXd+4fOnYAotcGPFD0peYo=');
} else {
callback();
}
},
- authType : 'digest'
+ authType : 'basic'
});
/**
@@ -29,7 +29,7 @@ var digest = auth({
*/
http.createServer(function(req, res) {
// Apply authentication to server.
- digest.apply(req, res, function(username) {
+ basic.apply(req, res, function(username) {
res.end("Welcome to private area - " + username + "!");
});
}).listen(1337);
101 lib/auth/basic.js
View
@@ -24,12 +24,14 @@ module.exports = Basic;
* @param {String} authRealm authentication realm.
* @param {Array} authUsers array of users.
*/
-function Basic(authRealm, authUsers) {
+function Basic(authRealm, authUsers, authHelper) {
// Realm.
this.realm = authRealm;
// Users.
this.users = authUsers;
-
+ // Authentication helper.
+ this.authHelper = authHelper;
+
// Used for async callback.
var self = this;
@@ -41,12 +43,13 @@ function Basic(authRealm, authUsers) {
* @param {Function} next function that will be called after user is authenticated.
*/
this.apply = function(request, response, next) {
- var authenticated = self.isAuthenticated(request);
- if(!authenticated) {
- self.ask(response);
- } else {
- next(authenticated);
- }
+ self.isAuthenticated(request, function(authenticated) {
+ if(!authenticated) {
+ self.ask(response);
+ } else {
+ next(authenticated);
+ }
+ });
}
};
@@ -54,10 +57,9 @@ function Basic(authRealm, authUsers) {
* Checks authorization header in request.
*
* @param {Request} request HTTP request object.
- * @return {Boolean} true if is authenticated, else false.
- * @return {String} the authenticated user ID, if authenticated, else undefined.
+ * @param {Function} callback after authentication is finished.
*/
-Basic.prototype.isAuthenticated = function(request) {
+Basic.prototype.isAuthenticated = function(request, callback) {
var authenticated = undefined;
// If header exists.
@@ -70,35 +72,66 @@ Basic.prototype.isAuthenticated = function(request) {
}
if (authHeader) {
- var header = request.headers.authorization;
+ var header = authHeader;
var clientUserString = header.split(" ")[1];
+ var clientUser = utils.decodeBase64(clientUserString).split(":");
+ var clientUserName = clientUser[0];
+ var clientPasswordHash = clientUser[1];
+ var authUsers = this.users;
+ var validateClientString = this.validateClientString;
- // Searching for user in user list.
- if(clientUserString) {
- var clientUser = utils.decodeBase64(clientUserString).split(":");
- var clientUserName = clientUser[0];
- var clientPasswordHash = clientUser[1];
+ // Searching for user in user list.
+ if (clientUserString) {
+ if (this.authHelper) {
+ this.authHelper(clientUserName, function(passwordHash) {
+ if ( passwordHash && htpasswd.validate(passwordHash, clientPasswordHash) ) {
+ callback(clientUserName);
+ } else {
+ callback();
+ }
+ });
+ } else {
+ callback(validateClientString(clientUserName, clientPasswordHash, authUsers));
+ }
+ } else {
+ callback();
+ }
+ } else {
+ callback();
+ }
- if(clientUserName && clientPasswordHash) {
- for(var i = 0; i < this.users.length; ++i) {
- var myUser = this.users[i].split(":");
- var myUserName = myUser[0];
- var myPasswordHash = myUser[1];
+ return authenticated;
+};
- // Ensure the username and password both match.
- if(myUserName === clientUserName) {
- if(htpasswd.validate(myPasswordHash, clientPasswordHash)) {
- authenticated = myUserName;
- break;
- }
- }
- }
- }
- }
- }
+/**
+ * Validates client username and client password hash.
+ *
+ * @param {String} username of client.
+ * @param {String} password hash of client.
+ * @return {String} the authenticated user ID, if authentication is successful, else undefined.
+ */
+Basic.prototype.validateClientString = function(clientUserName, clientPasswordHash, users) {
+ var authenticated = undefined;
- return authenticated;
+ if (clientUserName && clientPasswordHash) {
+ for (var i = 0; i < users.length; ++i) {
+ var myUser = users[i].split(":");
+ var myUserName = myUser[0];
+ var myPasswordHash = myUser[1];
+
+ // Ensure the username and password both match.
+ if (myUserName === clientUserName) {
+ if (htpasswd.validate(myPasswordHash, clientPasswordHash)) {
+ authenticated = myUserName;
+ break;
+ }
+ }
+ }
+ }
+
+ return authenticated;
};
+
/**
* Asks client for authentication.
*
2  lib/auth/digest.js
View
@@ -89,7 +89,7 @@ Digest.prototype.isAuthenticated = function(request, callback) {
}
if (authHeader) {
- var header = request.headers.authorization;
+ var header = authHeader;
var co = this.parseAuthHeader(header);
// Check for expiration.
2  lib/http-auth.js
View
@@ -15,7 +15,7 @@ var opt = require('./options');
*
* - authRealm authentication realm.
* - authHelper function that allows to override standard authentication method
- * by providing custom user loading mechanism. Only for digest.
+ * by providing custom user loading mechanism.
* - authFile file where user details are stored in format {user:pass}.
* - authList list where user details are stored in format {user:pass},
* ignored if authFile is specified.
2  lib/provider.js
View
@@ -17,7 +17,7 @@ module.exports = {
if(options && options.authType == 'digest') {
return new Digest(options.authRealm, options.authUsers, options.algorithm, options.authHelper);
} else if(options && options.authType == 'basic') {
- return new Basic(options.authRealm, options.authUsers);
+ return new Basic(options.authRealm, options.authUsers, options.authHelper);
} else {
throw new Error("Invalid type, may be digest | basic!");
}
26 tests/auth/test-basic.js
View
@@ -70,10 +70,10 @@ exports['testIsAuthenticatedTrue'] = function(test) {
var request = {headers : {authorization : header}};
// Source method call, that must return username.
- test.equals(source.isAuthenticated(request), "user", "User must be valid!");
-
- // Test is done.
- test.done();
+ source.isAuthenticated(request, function(authenticated) {
+ test.equals(authenticated, "user", "User must be valid!");
+ test.done();
+ });
};
/**
* Test for isAuthenticated, false case.
@@ -83,10 +83,10 @@ exports['testIsAuthenticatedFalse'] = function(test) {
var request = {headers : {authorization : "Basic: userhash4"}};
// Source method call, that must return false.
- test.ok(!source.isAuthenticated(request), "User must not be valid!");
-
- // Test is done.
- test.done();
+ source.isAuthenticated(request, function(authenticated) {
+ test.ok(!authenticated, "User must not be valid!");
+ test.done();
+ });
};
/**
* Test for isAuthenticated, false case, where the password
@@ -98,10 +98,10 @@ exports['testIsAuthenticatedFalseSamePassword'] = function(test) {
var request = {headers : {authorization : header}};
// Source method call, that must return false.
- test.ok(!source.isAuthenticated(request), "User must not be valid!");
-
- // Test is done.
- test.done();
+ source.isAuthenticated(request, function(authenticated) {
+ test.ok(!authenticated, "User must not be valid!");
+ test.done();
+ });
};
/**
* Test for apply, pass case.
@@ -148,4 +148,4 @@ exports['testApplyAuth'] = function(test) {
response.assert();
// Test is done.
test.done();
-};
+};
Please sign in to comment.
Something went wrong with that request. Please try again.