http-authNode.js package for HTTP basic and digest access authentication. | |
| lib/auth/basic.js |
Default setup module.
|
var defaults = require('../defaults');
|
Exporting module.
|
module.exports = Basic;
|
Basic Access Authentication.
|
function Basic(authRealm, authUsers) {
this.realm = authRealm;
this.users = authUsers;
var self = this;
|
Applies basic authentication and calls next after user is authenticated.
param: Request request HTTP request object. param: Response response HTTP response object. param: Function next function that will be called after user is authenticated.
|
this.apply = function(request, response, next) {
var authenticated = self.isAuthenticated(request, response);
if(!authenticated) {
self.ask(request, response);
} else {
next();
}
}};
|
Checks authorization header in request.
param: Request request HTTP request object. param: Response response HTTP response object. return: Boolean true if is authenticated, else false.
|
Basic.prototype.isAuthenticated = function(request, response) {
var authenticated = false;
if("authorization" in request.headers) {
var header = request.headers.authorization;
var user = header.split(" ")[1];
if(user) {
authenticated = this.users.indexOf(user) != -1;
}
}
return authenticated;
}
|
Asks client for authentication.
|
Basic.prototype.ask = function(request, response) {
var header = "Basic realm=\"" + this.realm + "\"";
response.setHeader("WWW-Authenticate", header);
response.writeHead(401);
response.end(defaults.HTML_401);
}
|
| lib/auth/digest.js |
Default setup module.
|
var defaults = require('../defaults');
|
Utility module.
|
var utils = require('../utils');
|
Module for generating unique id.
|
var uuid = require('node-uuid');
|
Exporting module.
|
module.exports = Digest;
|
Digest Access Authentication.
param: String authRealm authentication realm. param: Array authUsers array of users. param: String algorithm algorithm for authentication.
|
function Digest(authRealm, authUsers, algorithm) {
this.realm = authRealm;
this.users = authUsers;
this.nonces = new Array();
this.algorithm = algorithm;
var self = this;
|
* Applies digest authentication and calls next after user is authenticated.
*
* @param {Request} request HTTP request object.
* @param {Response} response HTTP response object.
* @param {Function} next function that will be called after user is authenticated.
|
this.apply = function(request, response, next) {
var authenticated = self.isAuthenticated(request, response, requestBody);
if(!authenticated) {
self.ask(request, response, requestBody);
} else {
next();
}
}
};
|
Checks authorization header in request.
param: Request request HTTP request object. param: Response response HTTP response object. param: String requestBody HTTP request body string. return: Boolean true if is authenticated, else false.
|
Digest.prototype.isAuthenticated = function(request, response, requestBody) {
var authenticated = false;
if("authorization" in request.headers) {
var header = request.headers.authorization;
var co = this.parseAuthHeader(header);
if(co.nonce in this.nonces) {
var ha2;
ha2 = utils.md5(request.method + ":" + co.uri);
var userHash = this.users[co.username];
if(userHash && typeof userHash === 'string') {
var ha1 = utils.md5(this.users[co.username]);
if(co.algorithm == 'MD5-sess') {
ha1 = utils.md5(ha1 + ":" + co.nonce + ":" + co.cnonce);
}
if(co.qop) {
if(co.nc > this.nonces[co.nonce]) {
this.nonces[co.nonce] = co.nc;
var authRes = utils.md5(ha1 + ":" + co.nonce + ":" + co.nc + ":" +
co.cnonce + ":" + co.qop + ":" + ha2);
authenticated = (authRes == co.response);
}
} else {
var authRes = utils.md5(ha1 + ":" + co.nonce + ":" + ha2);
authenticated = (authRes == co.response);
}
}
}
}
return authenticated;
}
|
Asks client for authentication.
param: Request request HTTP request object. param: Response response HTTP response object. param: String requestBody HTTP request body string.
|
Digest.prototype.ask = function(request, response, requestBody) {
var nonce = utils.md5(uuid());
this.nonces[nonce] = 0;
setTimeout(this.expireNonce, defaults.NONCE_EXPIRE_TIMEOUT, nonce, this.nonces);
var header = "Digest realm=\"" + this.realm + "\", qop=\"auth\", nonce=\"" + nonce + "\",
algorithm=\"" + this.algorithm + "\"";
response.setHeader("WWW-Authenticate", header);
response.writeHead(401);
response.end(defaults.HTML_401);
}
|
Method for clearing not used nonces.
|
Digest.prototype.expireNonce = function(nonce, nonces) {
delete nonces[nonce];
}
|
Method for parsing authorization header.
|
Digest.prototype.parseAuthHeader = function(header) {
var headerOptions = new Array();
var searchHeader = header.replace(/\\"/g, """);
searchHeader = searchHeader.replace(/(\w+)=([^," ]+)/g, '$1=\"$2\"');
var tokens = searchHeader.match(/(\w+)="([^"]+)"/g);
if(tokens) {
for(var i = 0; i < tokens.length; ++i) {
var token = tokens[i];
var equalIndex = token.indexOf("=");
var key = token.substr(0, equalIndex);
var value = token.substr(equalIndex + 2, token.length - equalIndex - 3);
headerOptions[key] = value;
}
}
return headerOptions;
}
|
| lib/defaults.js |
Module for default setups.
|
module.exports = {
|
* Default HTML for not authorized page.
|
'HTML_401' : "<!DOCTYPE html>\n<html><head><title>401 Unauthorized</title></head><body><h1>401 Unauthorized</h1><p>This page requires authorization.</p></body></html>",
|
* Nonce expire timeout.
|
'NONCE_EXPIRE_TIMEOUT' : 3600000,
|
* Default algorithm for Digest Access Authentication.
|
'DEFAULT_ALGO' : 'MD5'
}
|
| lib/http-auth.js |
Authentication provider.
|
var provider = require('./provider');
|
Parser module for authentication input options.
|
var opt = require('./options');
|
Requests authentication instance from provider.
|
module.exports = function(options) {
var parsedOptions = opt(options);
return provider.newInstance(parsedOptions);
};
|
| lib/options.js |
Modules.
|
var fs = require('fs'), utils = require('./utils'), defaults = require('./defaults');
|
Parsing input options for authentication.
|
module.exports = function(options) {
if(!options) {
throw new Error("Authentication options are empty!");
}
var authType = options['authType'];
if(!authType) {
authType = options['authType'] = 'digest';
}
if(!(authType in {'digest' : 1, 'basic' : 1})) {
throw new Error("Authentication type may be digest or basic!");
}
if(!options.algorithm) {
options['algorithm'] = defaults.DEFAULT_ALGO;
}
if(!(options.algorithm in {'MD5' : 1, 'MD5-sess' : 1})) {
throw new Error("Authentication algorithm may be MD5 or MD5-sess!");
}
var authRealm = options['authRealm'];
if(!authRealm) {
throw new Error("Authentication realm is mandatory!");
}
var authUsers = new Array();
var authList = options['authList'];
var authFile = options['authFile'];
if(authFile) {
authList = fs.readFileSync(authFile, 'UTF-8').toString().split('\n');
}
if(!authList || authList.length == 0) {
throw new Error("Authentication list cannot be empty!");
}
for(var i = 0; i < authList.length; ++i) {
var authLine = authList[i];
if(authType == 'digest') {
var authTokens = authLine.split(":");
authUsers[authTokens[0]] = authTokens[0] + ":" + authRealm + ":" + authTokens[1];
} else if(authType == 'basic') {
authUsers.push(utils.base64(authLine));
}
}
options['authUsers'] = authUsers;
return options;
}
|
| lib/provider.js |
Digest and basic modules.
|
var Digest = require('./auth/digest'), Basic = require('./auth/basic');
|
Provider creates new basic or digest authentication instance.
|
module.exports = {
|
Creates new authentication instance.
|
'newInstance' : function(options) {
if(options && options.authType == 'digest') {
return new Digest(options.authRealm, options.authUsers, options.algorithm);
} else if(options && options.authType == 'basic') {
return new Basic(options.authRealm, options.authUsers);
} else {
throw new Error("Invalid type, may be digest | basic!");
}
}};
|
| lib/utils.js |
Crypto module.
|
var crypto = require('crypto');
|
Utility module.
|
module.exports = {
|
Function for encoding string to base64.
|
'base64' : function(str) {
return new Buffer(str, 'UTF-8').toString('base64');
},
|
MD5 hash method.
|
'md5' : function(str) {
var hash = crypto.createHash('MD5');
hash.update(str);
return hash.digest('hex');
}};
|