Skip to content

Instantly share code, notes, and snippets.

@dstibrany
Created June 8, 2015 22:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dstibrany/8eb6c48f017fb3904e2b to your computer and use it in GitHub Desktop.
Save dstibrany/8eb6c48f017fb3904e2b to your computer and use it in GitHub Desktop.
auth module of knox library
"use strict";
/*!
* knox - auth
* Copyright(c) 2010 LearnBoost <dev@learnboost.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var crypto = require('crypto')
, parse = require('url').parse;
/**
* Query string params permitted in the canonicalized resource.
* @see http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html#ConstructingTheCanonicalizedResourceElement
*/
var whitelist = [
'acl'
, 'delete'
, 'lifecycle'
, 'location'
, 'logging'
, 'notification'
, 'partNumber'
, 'policy'
, 'requestPayment'
, 'torrent'
, 'uploadId'
, 'uploads'
, 'versionId'
, 'versioning'
, 'versions'
, 'website'
];
/**
* Return an "Authorization" header value with the given `options`
* in the form of "AWS <key>:<signature>"
*
* @param {Object} options
* @return {String}
* @api private
*/
exports.authorization = function(options){
return 'AWS ' + options.key + ':' + exports.sign(options);
};
/**
* Simple HMAC-SHA1 Wrapper
*
* @param {Object} options
* @return {String}
* @api private
*/
exports.hmacSha1 = function(options){
return crypto.createHmac('sha1', options.secret).update(new Buffer(options.message, 'utf-8')).digest('base64');
};
/**
* Create a base64 sha1 HMAC for `options`.
*
* @param {Object} options
* @return {String}
* @api private
*/
exports.sign = function(options){
options.message = exports.stringToSign(options);
return exports.hmacSha1(options);
};
/**
* Create a base64 sha1 HMAC for `options`.
*
* Specifically to be used with S3 presigned URLs
*
* @param {Object} options
* @return {String}
* @api private
*/
exports.signQuery = function(options){
options.message = exports.queryStringToSign(options);
return exports.hmacSha1(options);
};
/**
* Return a string for sign() with the given `options`.
*
* Spec:
*
* <verb>\n
* <md5>\n
* <content-type>\n
* <date>\n
* [headers\n]
* <resource>
*
* @param {Object} options
* @return {String}
* @api private
*/
exports.stringToSign = function(options){
var headers = options.amazonHeaders || '';
if (headers) headers += '\n';
return [
options.verb
, options.md5
, options.contentType
, options.date instanceof Date ? options.date.toUTCString() : options.date
, headers + options.resource
].join('\n');
};
/**
* Return a string for sign() with the given `options`, but is meant exclusively
* for S3 presigned URLs
*
* Spec:
*
* <verb>\n\n
* <contentType or nothing>\n
* <date>\n
* <x-amz-security-token header>\n --- optional
* <resource>
*
* @param {Object} options
* @return {String}
* @api private
*/
exports.queryStringToSign = function(options){
return (options.verb || 'GET') + '\n\n' +
(typeof options.contentType !== 'undefined' ?
options.contentType : '') + '\n' +
options.date + '\n' +
(typeof options.extraHeaders !== 'undefined' ?
exports.canonicalizeHeaders(options.extraHeaders) + '\n' : '') +
(typeof options.token !== 'undefined' ?
'x-amz-security-token:' + options.token + '\n' : '') +
options.resource;
};
/**
* Perform the following:
*
* - ignore non-amazon headers
* - lowercase fields
* - sort lexicographically
* - trim whitespace between ":"
* - join with newline
*
* @param {Object} headers
* @return {String}
* @api private
*/
exports.canonicalizeHeaders = function(headers){
var buf = []
, fields = Object.keys(headers);
for (var i = 0, len = fields.length; i < len; ++i) {
var field = fields[i]
, val = headers[field];
field = field.toLowerCase();
if (field.indexOf('x-amz') !== 0 || field === 'x-amz-date') {
continue;
}
buf.push(field + ':' + val);
}
var headerSort = function(a, b) {
// Headers are sorted lexigraphically based on the header name only.
a = a.split(":")[0]
b = b.split(":")[0]
return a > b ? 1 : -1;
}
return buf.sort(headerSort).join('\n');
};
/**
* Perform the following:
*
* - ignore non sub-resources
* - sort lexicographically
*
* @param {String} a URI-encoded resource (path + query string)
* @return {String}
* @api private
*/
exports.canonicalizeResource = function(resource){
var url = parse(resource, true)
, path = url.pathname
, buf = [];
// apply the query string whitelist
Object.keys(url.query).forEach(function (key) {
if (whitelist.indexOf(key) != -1) {
buf.push(key + (url.query[key] ? "=" + url.query[key] : ''));
}
});
return path + (buf.length
? '?' + buf.sort().join('&')
: '');
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment