Created
October 10, 2017 21:51
-
-
Save acamino/91d21da5dbb88be5d8a69e4a48c9d045 to your computer and use it in GitHub Desktop.
Secure your C# / ASP.NET Core by validating incoming Twilio Requests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Mvc; | |
using Twilio.TwiML; | |
using ValidateRequestExample.Filters; | |
namespace ValidateRequestExample.Controllers | |
{ | |
public class IncomingController : Controller | |
{ | |
[ValidateTwilioRequest] | |
[Produces("text/xml")] | |
public IActionResult Voice(string from) | |
{ | |
var message = "Thanks for calling! " + | |
$"Your phone number is {from}. " + | |
"I got your call because of Twilio\'s webhook. " + | |
"Goodbye!"; | |
var response = new VoiceResponse(); | |
response.Say(string.Format(message, from)); | |
response.Hangup(); | |
return Content(response.ToString()); | |
} | |
[ValidateTwilioRequest] | |
[Produces("text/xml")] | |
public IActionResult Message(string body) | |
{ | |
var message = $"Your text to me was {body.Length} characters long. " + | |
"Webhooks are neat :)"; | |
var response = new MessagingResponse(); | |
response.Message(new Message(message)); | |
return Content(response.ToString()); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Mvc.Filters; | |
using Microsoft.Extensions.Configuration; | |
using Twilio.Security; | |
namespace ValidateRequestExample.Filters | |
{ | |
[AttributeUsage(AttributeTargets.Method)] | |
public class ValidateTwilioRequestAttribute : ActionFilterAttribute | |
{ | |
private readonly RequestValidator _requestValidator; | |
private static IConfigurationRoot Configuration => | |
new ConfigurationBuilder() | |
.SetBasePath(Directory.GetCurrentDirectory()) | |
.AddJsonFile("appsettings.json", true, true).Build(); | |
public ValidateTwilioRequestAttribute() | |
{ | |
var authToken = Configuration["TwilioAuthToken"]; | |
_requestValidator = new RequestValidator(authToken); | |
} | |
public override void OnActionExecuting(ActionExecutingContext actionContext) | |
{ | |
var context = actionContext.HttpContext; | |
if (!IsValidRequest(context.Request)) | |
{ | |
actionContext.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden; | |
} | |
base.OnActionExecuting(actionContext); | |
} | |
private bool IsValidRequest(HttpRequest request) { | |
var requestUrl = RequestRawUrl(request); | |
var parameters = ToDictionary(request.Form); | |
var signature = request.Headers["X-Twilio-Signature"]; | |
return _requestValidator.Validate(requestUrl, parameters, signature); | |
} | |
private static string RequestRawUrl(HttpRequest request) | |
{ | |
return $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"; | |
} | |
private static IDictionary<string, string> ToDictionary(IFormCollection collection) | |
{ | |
return collection.Keys | |
.Select(key => new { Key = key, Value = collection[key] }) | |
.ToDictionary(p => p.Key, p => p.Value.ToString()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment