You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					155 lines
				
				5.9 KiB
			
		
		
			
		
	
	
					155 lines
				
				5.9 KiB
			| 
											3 years ago
										 | var crypto = require('crypto'), | ||
|  |     _ = require('underscore'), | ||
|  |     scmp = require('scmp'), | ||
|  |     url = require('url'); | ||
|  | 
 | ||
|  | /** | ||
|  |  Utility function to validate an incoming request is indeed from Twilio | ||
|  | 
 | ||
|  |  @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 | ||
|  |  */ | ||
|  | exports.validateRequest = function(authToken, twilioHeader, url, params) { | ||
|  |     Object.keys(params).sort().forEach(function(key, i) { | ||
|  |         url = url + key + params[key]; | ||
|  |     }); | ||
|  | 
 | ||
|  |     return scmp(twilioHeader, crypto.createHmac('sha1', authToken).update(new Buffer(url, 'utf-8')).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 | ||
|  |  @param {object} options - options for request validation: | ||
|  |     - webhookUrl: The full URL (with query string) you used to configure the webhook with Twilio - overrides host/protocol options | ||
|  |     - host: manually specify the host name used by Twilio in a number's webhook config | ||
|  |     - protocol: manually specify the protocol used by Twilio in a number's webhook config | ||
|  |  */ | ||
|  | exports.validateExpressRequest = function(request, authToken, opts) { | ||
|  |     var options = opts||{}, webhookUrl; | ||
|  |     if (options.url) { | ||
|  |         // Let the user specify the full URL
 | ||
|  |         webhookUrl = options.url; | ||
|  |     } else { | ||
|  |         // Use configured host/protocol, or infer based on request
 | ||
|  |         var protocol = options.protocol||request.protocol; | ||
|  |         var host = options.host||request.headers.host; | ||
|  |         webhookUrl = url.format({ | ||
|  |             protocol: protocol, | ||
|  |             host: host, | ||
|  |             pathname: request.originalUrl | ||
|  |         }); | ||
|  |     } | ||
|  |      | ||
|  |     return exports.validateRequest( | ||
|  |         authToken,  | ||
|  |         request.header('X-Twilio-Signature'),  | ||
|  |         webhookUrl,  | ||
|  |         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. | ||
|  | - host: manually specify the host name used by Twilio in a number's webhook config | ||
|  | - protocol: manually specify the protocol used by Twilio in a number's webhook config | ||
|  | 
 | ||
|  | 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
 | ||
|  | }); | ||
|  | var webhookMiddleware = twilio.webhook({ | ||
|  |     host: 'hook.twilio.com', | ||
|  |     protocol: 'https' | ||
|  | }); | ||
|  |  */ | ||
|  | exports.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') | ||
|  |                     .status(500) | ||
|  |                     .send('Webhook Error - we attempted to validate this request without first configuring our auth token.'); | ||
|  |             } else { | ||
|  |                 // Check that the request originated from Twilio
 | ||
|  |                 valid = exports.validateExpressRequest(request, opts.authToken, { | ||
|  |                     url: opts.url, | ||
|  |                     host: opts.host, | ||
|  |                     protocol: opts.protocol | ||
|  |                 }); | ||
|  |                 if (valid) { | ||
|  |                     next(); | ||
|  |                 } else { | ||
|  |                     return response | ||
|  |                         .type('text/plain') | ||
|  |                         .status(403) | ||
|  |                         .send('Twilio Request Validation Failed.'); | ||
|  |                 } | ||
|  |             } | ||
|  |         } else { | ||
|  |             next(); | ||
|  |         } | ||
|  |     }; | ||
|  | }; |