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
						
					
					
				| 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();
 | |
|         }
 | |
|     };
 | |
| }; |