diff --git a/lib/rack/twilio_webhook_authentication.rb b/lib/rack/twilio_webhook_authentication.rb index 364decbd7..2337fe02f 100644 --- a/lib/rack/twilio_webhook_authentication.rb +++ b/lib/rack/twilio_webhook_authentication.rb @@ -17,18 +17,20 @@ module Rack # doesn't validate then the middleware responds immediately with a 403 status. class TwilioWebhookAuthentication - def initialize(app, auth_token, *paths) + 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) end def call(env) return @app.call(env) unless env["PATH_INFO"].match(@path_regex) - validator = Twilio::Util::RequestValidator.new(@auth_token) request = Rack::Request.new(env) original_url = request.url params = request.post? ? request.POST : {} + auth_token = @auth_token || get_auth_token(params['AccountSid']) + validator = Twilio::Util::RequestValidator.new(auth_token) signature = env['HTTP_X_TWILIO_SIGNATURE'] || "" if validator.validate(original_url, params, signature) @app.call(env) @@ -41,4 +43,5 @@ def call(env) end end end + end diff --git a/spec/rack/twilio_webhook_authentication_spec.rb b/spec/rack/twilio_webhook_authentication_spec.rb index ed1ee4a6b..63f53a6ee 100644 --- a/spec/rack/twilio_webhook_authentication_spec.rb +++ b/spec/rack/twilio_webhook_authentication_spec.rb @@ -18,6 +18,28 @@ Rack::TwilioWebhookAuthentication.new(@app, 'ABC', /\/voice/, /\/sms/) }.not_to raise_error end + + it 'should initialize with an app, dynamic token and paths' do + expect { + Rack::TwilioWebhookAuthentication.new(@app, nil, /\/voice/, /\/sms/) + }.not_to raise_error + end + end + + describe 'calling against one path with dynamic auth token' do + it 'should allow a request through if it validates' do + auth_token = 'qwerty' + account_sid = 12345 + expect_any_instance_of(Rack::Request).to receive(:post?).and_return(true) + expect_any_instance_of(Rack::Request).to receive(:POST).and_return({'AccountSid' => account_sid}) + @middleware = Rack::TwilioWebhookAuthentication.new(@app, nil, /\/voice/) { |asid| auth_token} + request_validator = double('RequestValidator') + expect(Twilio::Util::RequestValidator).to receive(:new).with(auth_token).and_return(request_validator) + expect(request_validator).to receive(:validate).and_return(true) + request = Rack::MockRequest.env_for('/voice') + status, headers, body = @middleware.call(request) + expect(status).to be(200) + end end describe 'calling against one path' do