lib/rack/twilio_webhook_authentication.rb in twilio-ruby-5.63.0 vs lib/rack/twilio_webhook_authentication.rb in twilio-ruby-5.63.1

- old
+ new

@@ -1,7 +1,9 @@ # frozen_string_literal: true +require 'rack/media_type' + module Rack # Middleware that authenticates webhooks from Twilio using the request # validator. # # The middleware takes an auth token with which to set up the request @@ -17,10 +19,14 @@ # the ENV and only against paths that match /\/messages/. If the request # validates then it gets passed on to the action as normal. If the request # doesn't validate then the middleware responds immediately with a 403 status. class TwilioWebhookAuthentication + # Rack's FORM_DATA_MEDIA_TYPES can be modified to taste, so we're slightly + # more conservative in what we consider form data. + FORM_URLENCODED_MEDIA_TYPE = Rack::MediaType.type('application/x-www-form-urlencoded') + def initialize(app, auth_token, *paths, &auth_token_lookup) @app = app @auth_token = auth_token define_singleton_method(:get_auth_token, auth_token_lookup) if block_given? @path_regex = Regexp.union(paths) @@ -28,11 +34,11 @@ def call(env) return @app.call(env) unless env['PATH_INFO'].match(@path_regex) request = Rack::Request.new(env) original_url = request.url - params = request.post? ? request.POST : {} + params = extract_params!(request) auth_token = @auth_token || get_auth_token(params['AccountSid']) validator = Twilio::Security::RequestValidator.new(auth_token) signature = env['HTTP_X_TWILIO_SIGNATURE'] || '' if validator.validate(original_url, params, signature) @app.call(env) @@ -42,7 +48,25 @@ { 'Content-Type' => 'text/plain' }, ['Twilio Request Validation Failed.'] ] end end + + # Extract the params from the the request that we can use to determine the + # signature. This _may_ modify the passed in request since it may read/rewind + # the body. + def extract_params!(request) + return {} unless request.post? + + if request.media_type == FORM_URLENCODED_MEDIA_TYPE + request.POST + else + request.body.rewind + body = request.body.read + request.body.rewind + body + end + end + + private :extract_params! end end