我已经根据AuthorizationHandler实现了一个官方的“Twilio教程”,但是它只适用于与短信相关的请求,而不适用于与语音相关的请求(验证总是失败)。
下面是唯一应用于不同控制器的AuthorizationHandler,这些控制器接受Twilio的POST请求,将入站和出站语音呼叫、入站短消息和状态更改通知我的API:
public class TwilioInboundRequestAuthorizationHandler : AuthorizationHandler<TwilioInboundRequestRequirement>
{
private readonly RequestValidator _requestValidator;
public TwilioInboundRequestAuthorizationHandler(IOptionsSnapshot<AppOptions> options)
{
// Initialize the validator
_requestValidator = new RequestValidator(options.Value.TwilioAuthToken);
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TwilioInboundRequestRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext mvcContext)
{
// Examine MVC-specific things like routing data.
HttpRequest httpRequest = mvcContext.HttpContext.Request;
if (IsValidRequest(httpRequest))
{
context.Succeed(requirement);
}
else
{
/* Omitted some code that logs the error to a cloud service */
context.Fail();
}
}
else
{
throw new NotImplementedException();
}
// Check if the requirement is fulfilled.
return Task.CompletedTask;
}
private bool IsValidRequest(HttpRequest request) {
// The Twilio request URL
var requestUrl = RequestRawUrl(request);
var parameters = ToDictionary(request.Form);
// The X-Twilio-Signature header attached to the request
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());
}
}
public class TwilioInboundRequestRequirement : IAuthorizationRequirement
{
}编辑:
根据Twilio支持的建议,我应该更改RequestRawUrl以删除URL中的端口号。然而,这会导致验证只适用于语音通话,而对于SMS则不再起作用(与最初的问题相反)。我怀疑Twilio在语音或短信的请求头中设置了错误的签名。
我将RequestRawUrl函数改为
private static string RequestRawUrl(HttpRequest request)
{
return $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}";
}至
private static string RequestRawUrl(HttpRequest request)
{
return $"{request.Scheme}://{request.Host.Host}{request.Path}{request.QueryString}";
}发布于 2019-01-07 21:11:32
我们也有过类似的问题。归根结底,对于某些方法(如SMS),请求URL以https的形式出现,而其他方法(如TwiML语音脚本回调)则以http的形式出现。
如果请求以http写成,则.Validate(.)方法将失败,即使它是有效的请求。
因此,为了让Twilio请求验证器工作,我们只需重写请求URL。
private bool IsValidRequest(HttpRequestBase request)
{
var signature = request.Headers["X-Twilio-Signature"];
Debug.WriteLine(request.Headers["X-Twilio-Signature"]);
var requestUrl = rewriteUri(request.Url.AbsoluteUri);
Debug.WriteLine("URI is: " + rewriteUri(request.Url.AbsoluteUri));
return _requestValidator.Validate(requestUrl, request.Form, signature);
}
private string rewriteUri(string absoluteUri)
{
//check to make sure we're not replacing 'https' with 'httpss'
if (!absoluteUri.Contains("https"))
{
return Regex.Replace(absoluteUri, @"http", "https");
}
return absoluteUri;
}发布于 2021-10-20 18:19:34
在本地运行ngrok时,我遇到了完全相同的问题,但在部署时却不是这样。我发现RequestValidator需要原始ngrok,但默认情况下我们要传递localhost。最后我解决了这个问题:
private bool IsValidRequest(HttpRequest request)
{
var requestUrl = RequestRawUrl(request);
var parameters = ToDictionary(request.Form);
var signature = request.Headers["X-Twilio-Signature"];
// Check if we are running locally and need to pass ngrok through for validation to succeed.
if (request.Headers.ContainsKey("X-Original-Host") && request.Headers["X-Original-Host"][0].Contains("ngrok"))
{
requestUrl = requestUrl.Replace(request.Headers["Host"][0], request.Headers["X-Original-Host"][0]);
}
return _requestValidator.Validate(requestUrl, parameters, signature);
}发布于 2019-03-21 22:57:10
如果您使用ngrok,验证器希望ngrok (我认为与其他代理相同)在HTTP_X_ORIGINAL_HOST中可以获得原始主机,也许这会对您有所帮助。
https://stackoverflow.com/questions/49868419
复制相似问题