// Copyright 2012 Mark Cavage, Inc. All rights reserved.
|
|
'use strict';
|
|
var httpSignature = require('http-signature');
|
var errors = require('restify-errors');
|
|
///--- Globals
|
|
var InvalidHeaderError = errors.InvalidHeaderError;
|
|
var OPTIONS = {
|
algorithms: [
|
'rsa-sha1',
|
'rsa-sha256',
|
'rsa-sha512',
|
'dsa-sha1',
|
'hmac-sha1',
|
'hmac-sha256',
|
'hmac-sha512'
|
]
|
};
|
|
///--- Helpers
|
|
function parseBasic(string) {
|
var decoded;
|
var index;
|
var pieces;
|
|
decoded = new Buffer(string, 'base64').toString('utf8');
|
|
if (!decoded) {
|
throw new InvalidHeaderError('Authorization header invalid');
|
}
|
|
index = decoded.indexOf(':');
|
|
if (index === -1) {
|
pieces = [decoded];
|
} else {
|
pieces = [decoded.slice(0, index), decoded.slice(index + 1)];
|
}
|
|
if (!pieces || typeof pieces[0] !== 'string') {
|
throw new InvalidHeaderError('Authorization header invalid');
|
}
|
|
// Allows for usernameless authentication
|
if (!pieces[0]) {
|
pieces[0] = null;
|
}
|
|
// Allows for passwordless authentication
|
if (!pieces[1]) {
|
pieces[1] = null;
|
}
|
|
return {
|
username: pieces[0],
|
password: pieces[1]
|
};
|
}
|
|
function parseSignature(request, options) {
|
var opts = options || {};
|
opts.algorithms = OPTIONS.algorithms;
|
|
try {
|
return httpSignature.parseRequest(request, options);
|
} catch (e) {
|
throw new InvalidHeaderError(
|
'Authorization header invalid: ' + e.message
|
);
|
}
|
}
|
|
/**
|
* Parses out the `Authorization` header as best restify can.
|
* Currently only HTTP Basic Auth and
|
* [HTTP Signature](https://github.com/joyent/node-http-signature)
|
* schemes are supported.
|
*
|
* @public
|
* @function authorizationParser
|
* @throws {InvalidArgumentError}
|
* @param {Object} [options] - an optional options object that is
|
* passed to http-signature
|
* @returns {Function} Handler
|
* @example
|
* <caption>
|
* Subsequent handlers will see `req.authorization`, which looks like above.
|
*
|
* `req.username` will also be set, and defaults to 'anonymous'. If the scheme
|
* is unrecognized, the only thing available in `req.authorization` will be
|
* `scheme` and `credentials` - it will be up to you to parse out the rest.
|
* </caption>
|
* {
|
* scheme: "<Basic|Signature|...>",
|
* credentials: "<Undecoded value of header>",
|
* basic: {
|
* username: $user
|
* password: $password
|
* }
|
* }
|
*/
|
function authorizationParser(options) {
|
function parseAuthorization(req, res, next) {
|
req.authorization = {};
|
req.username = 'anonymous';
|
|
if (!req.headers.authorization) {
|
return next();
|
}
|
|
var pieces = req.headers.authorization.split(' ', 2);
|
|
if (!pieces || pieces.length !== 2) {
|
var e = new InvalidHeaderError('BasicAuth content is invalid.');
|
return next(e);
|
}
|
|
req.authorization.scheme = pieces[0];
|
req.authorization.credentials = pieces[1];
|
|
try {
|
switch (pieces[0].toLowerCase()) {
|
case 'basic':
|
req.authorization.basic = parseBasic(pieces[1]);
|
req.username = req.authorization.basic.username;
|
break;
|
|
case 'signature':
|
req.authorization.signature = parseSignature(req, options);
|
req.username = req.authorization.signature.keyId;
|
break;
|
|
default:
|
break;
|
}
|
} catch (e2) {
|
return next(e2);
|
}
|
|
return next();
|
}
|
|
return parseAuthorization;
|
}
|
|
module.exports = authorizationParser;
|