Comparing version 1.5.0 to 1.6.0
120
lib/index.js
@@ -9,4 +9,3 @@ /** | ||
var crypto = require('crypto'), | ||
_ = require('underscore'), | ||
var webhooks = require('./webhooks'), | ||
RestClient = require('./RestClient'); | ||
@@ -24,117 +23,8 @@ | ||
/** | ||
Utility function to validate an incoming request is indeed from Twilio | ||
// Seup webhook helper functionality | ||
initializer.validateRequest = webhooks.validateRequest; | ||
initializer.validateExpressRequest = webhooks.validateExpressRequest; | ||
initializer.webhook = webhooks.webhook; | ||
@param {string} authToken - The auth token, as seen in the Twilio portal | ||
@param {string} twilioHeader - The value of the X-Twilio-Signature header from the request | ||
@param {string} url - The full URL (with query string) you configured to handle this request | ||
@param {object} params - the parameters sent with this request | ||
*/ | ||
initializer.validateRequest = function(authToken, twilioHeader, url, params) { | ||
Object.keys(params).sort().forEach(function(key, i) { | ||
url = url + key + params[key]; | ||
}); | ||
var computed = crypto.createHmac('sha1', authToken).update(url).digest('Base64'); | ||
return twilioHeader === crypto.createHmac('sha1', authToken).update(url).digest('Base64'); | ||
}; | ||
/** | ||
Utility function to validate an incoming request is indeed from Twilio (for use with express). | ||
adapted from https://github.com/crabasa/twiliosig | ||
@param {object} request - An expressjs request object (http://expressjs.com/api.html#req.params) | ||
@param {string} authToken - The auth token, as seen in the Twilio portal | ||
*/ | ||
initializer.validateExpressRequest = function(request, authToken) { | ||
var url = request.protocol + '://' + request.headers.host + request.originalUrl; | ||
return initializer.validateRequest(authToken, request.header('X-Twilio-Signature'), url, request.body||{}); | ||
}; | ||
/** | ||
Express middleware to accompany a Twilio webhook. Provides Twilio | ||
request validation, and makes the response a little more friendly for our | ||
TwiML generator. Request validation requires the express.urlencoded middleware | ||
to have been applied (e.g. app.use(express.urlencoded()); in your app config). | ||
Options: | ||
- validate: {Boolean} whether or not the middleware should validate the request | ||
came from Twilio. Default true. If the request does not originate from | ||
Twilio, we will return a text body and a 403. If there is no configured | ||
auth token and validate=true, this is an error condition, so we will return | ||
a 500. | ||
- includeHelpers: {Boolean} add helpers to the response object to improve support | ||
for XML (TwiML) rendering. Default true. | ||
Returns a middleware function. | ||
Examples: | ||
var webhookMiddleware = twilio.webhook(); | ||
var webhookMiddleware = twilio.webhook('asdha9dhjasd'); //init with auth token | ||
var webhookMiddleware = twilio.webhook({ | ||
validate:false // don't attempt request validation | ||
}); | ||
*/ | ||
initializer.webhook = function() { | ||
var opts = { | ||
validate:true, | ||
includeHelpers:true | ||
}; | ||
// Process arguments | ||
var tokenString; | ||
for (var i = 0, l = arguments.length; i<l; i++) { | ||
var arg = arguments[i]; | ||
if (typeof arg === 'string') { | ||
tokenString = arg; | ||
} else { | ||
opts = _.extend(opts, arg); | ||
} | ||
} | ||
// set auth token from input or environment variable | ||
opts.authToken = tokenString ? tokenString : process.env.TWILIO_AUTH_TOKEN; | ||
// Create middleware function | ||
return function hook(request, response, next) { | ||
// Add helpers, unless disabled | ||
if (opts.includeHelpers) { | ||
var oldSend = response.send; | ||
response.send = function() { | ||
// This is a special TwiML-aware version of send. If we detect | ||
// A twiml response object, we'll set the content-type and | ||
// automatically call .toString() | ||
if (arguments.length == 1 && arguments[0].legalNodes) { | ||
response.type('text/xml'); | ||
oldSend.call(response,arguments[0].toString()); | ||
} else { | ||
// Continue with old version of send | ||
oldSend.apply(response,arguments); | ||
} | ||
}; | ||
} | ||
// Do validation if requested | ||
if (opts.validate) { | ||
// Check for a valid auth token | ||
if (!opts.authToken) { | ||
console.error('[Twilio]: Error - Twilio auth token is required for webhook request validation.'); | ||
response.type('text/plain'); | ||
response.send(500, 'Webhook Error - we attempted to validate this request without first configuring our auth token.'); | ||
} else { | ||
// Check that the request originated from Twilio | ||
valid = initializer.validateExpressRequest(request,opts.authToken); | ||
if (valid) { | ||
next(); | ||
} else { | ||
response.type('text/plain'); | ||
return response.send(403, 'Twilio Request Validation Failed.'); | ||
} | ||
} | ||
} else { | ||
next(); | ||
} | ||
}; | ||
}; | ||
//public module interface is a function, which passes through to RestClient constructor | ||
module.exports = initializer; |
@@ -51,2 +51,3 @@ /** | ||
this.apiVersion = options.apiVersion || defaultApiVersion; | ||
this.timeout = options.timeout || 31000; // request timeout in milliseconds | ||
@@ -82,2 +83,33 @@ //REST Resource - shorthand for just "account" and "accounts" to match the REST API | ||
//process data and make available in a more JavaScripty format | ||
function processKeys(source) { | ||
if (_.isObject(source)) { | ||
Object.keys(source).forEach(function(key) { | ||
//Supplement underscore values with camel-case | ||
if (key.indexOf('_') > 0) { | ||
var cc = key.replace(/_([a-z])/g, function (g) { | ||
return g[1].toUpperCase() | ||
}); | ||
source[cc] = source[key]; | ||
} | ||
//process any nested arrays... | ||
if (Array.isArray(source[key])) { | ||
source[key].forEach(processKeys); | ||
} | ||
else if (_.isObject(source[key])) { | ||
processKeys(source[key]); | ||
} | ||
}); | ||
//Look for and convert date strings for specific keys | ||
['startDate', 'endDate', 'dateCreated', 'dateUpdated', 'startTime', 'endTime'].forEach(function(dateKey) { | ||
if (source[dateKey]) { | ||
source[dateKey] = new Date(source[dateKey]); | ||
} | ||
}); | ||
} | ||
} | ||
/** | ||
@@ -115,3 +147,3 @@ Get the base URL which we'll use for all requests with this client | ||
}; | ||
options.timeout = 31000; | ||
options.timeout = client.timeout; | ||
@@ -153,36 +185,11 @@ // Manually create POST body if there's a form object. Sadly, request | ||
//process data and make available in a more JavaScripty format | ||
function processKeys(source) { | ||
if (_.isObject(source)) { | ||
Object.keys(source).forEach(function(key) { | ||
//Supplement underscore values with camel-case | ||
if (key.indexOf('_') > 0) { | ||
var cc = key.replace(/_([a-z])/g, function (g) { | ||
return g[1].toUpperCase() | ||
}); | ||
source[cc] = source[key]; | ||
} | ||
//process any nested arrays... | ||
if (Array.isArray(source[key])) { | ||
source[key].forEach(processKeys); | ||
} | ||
else if (_.isObject(source[key])) { | ||
processKeys(source[key]); | ||
} | ||
}); | ||
//Look for and convert date strings for specific keys | ||
['startDate', 'endDate', 'dateCreated', 'dateUpdated', 'startTime', 'endTime'].forEach(function(dateKey) { | ||
if (source[dateKey]) { | ||
source[dateKey] = new Date(source[dateKey]); | ||
} | ||
}); | ||
} | ||
} | ||
processKeys(data); | ||
//hang response off the JSON-serialized data | ||
data.nodeClientResponse = response; | ||
//hang response off the JSON-serialized data, as unenumerable to allow for stringify. | ||
Object.defineProperty(data, 'nodeClientResponse', { | ||
value: response, | ||
configurable: true, | ||
writeable: true, | ||
enumerable: false | ||
}); | ||
@@ -189,0 +196,0 @@ // Resolve promise |
{ | ||
"name": "twilio", | ||
"description": "A Twilio helper library", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"author": "Kevin Whinnery <kevin.whinnery@gmail.com>", | ||
@@ -34,4 +34,4 @@ "contributors": [ | ||
"engines": { | ||
"node": ">=0.6.0" | ||
"node": ">=0.8.0" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
56421
30
1084