语音呼叫的Twilio请求验证始终失败(但适用于SMS)

时间:2018-04-17 01:20:01

标签: c# twilio asp.net-core-2.0 asp.net-core-webapi twilio-api

我已根据an official Twilio tutorial实施了AuthorizationHandler,但它仅适用于与SMS相关的请求,但不适用于与语音相关的请求(始终无法通过验证)。

下面是应用于不同控制器的唯一AuthorizationHandler,这些控制器接受来自Twilio的POST请求,以通知我的API入站和出站语音呼叫,入站SMS以及状态更改为出站SMS:

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}";
}

2 个答案:

答案 0 :(得分:0)

我们有类似的问题。归结为,对于某些方法(例如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;
    }

答案 1 :(得分:-1)

如果您使用ngrok,则验证程序将使用ngrok URL(我认为与其他代理相同) 在PHP中,原始主机可以从HTTP_X_ORIGINAL_HOST获取,也许这可以以某种方式帮助您