twilio-ruby-authenticate-webhooks 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dc8744d924736377123f5b2b66e9366b5711721cd57a272128864e068e3df20f
4
+ data.tar.gz: 62c63f5230c6de87cf41c535fd8dfa6f208ce03ad6e68fc8e011b5966f175d65
5
+ SHA512:
6
+ metadata.gz: 5e8238b7e4f003580db44a1a644c05182487de4a3df691fc79e94fdd5c54508e65735275b364d5e3bbbf6f6e85cce5ad258ab300c83b676da5eb24fe0ce339d7
7
+ data.tar.gz: 92d509308e9b55f714ff81b5233e879b955c1106b458a79783d8b984ae3c46267ef5b435a132f722003aa1ebfb9cfaa54738b50617d494aab9c5d666d84eddaf
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in twilio-ruby-authenticate-webhooks.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Erik Griffin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # TwilioRubyAuthenticateWebhooks
2
+
3
+ This Gem aims to provide functionality solely to authenticate Twilio Webhook requests in Rails and Rack applications. This was created in response to the large memory usage of the [twilio-ruby gem](https://github.com/twilio/twilio-ruby), as referenced in [#396](https://github.com/twilio/twilio-ruby/issues/396).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'twilio-ruby-authenticate-webhooks'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install twilio-ruby-authenticate-webhooks
20
+
21
+ ## Usage
22
+
23
+ https://www.twilio.com/docs/usage/tutorials/how-to-secure-your-sinatra-app-by-validating-incoming-twilio-requests
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pathrise-eng/twilio-ruby-authenticate-webhooks.
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "twilio/ruby/authenticate/webhooks"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ # Middleware that authenticates webhooks from Twilio using the request
5
+ # validator.
6
+ #
7
+ # The middleware takes an auth token with which to set up the request
8
+ # validator and any number of paths. When a path matches the incoming request
9
+ # path, the request will be checked for authentication.
10
+ #
11
+ # Example:
12
+ #
13
+ # require 'rack'
14
+ # use Rack::TwilioWebhookAuthentication, ENV['AUTH_TOKEN'], /\/messages/
15
+ #
16
+ # The above appends this middleware to the stack, using an auth token saved in
17
+ # the ENV and only against paths that match /\/messages/. If the request
18
+ # validates then it gets passed on to the action as normal. If the request
19
+ # doesn't validate then the middleware responds immediately with a 403 status.
20
+
21
+ class TwilioWebhookAuthentication
22
+ def initialize(app, auth_token, *paths, &auth_token_lookup)
23
+ @app = app
24
+ @auth_token = auth_token
25
+ define_singleton_method(:get_auth_token, auth_token_lookup) if block_given?
26
+ @path_regex = Regexp.union(paths)
27
+ end
28
+
29
+ def call(env)
30
+ return @app.call(env) unless env['PATH_INFO'].match(@path_regex)
31
+ request = Rack::Request.new(env)
32
+ original_url = request.url
33
+ params = request.post? ? request.POST : {}
34
+ auth_token = @auth_token || get_auth_token(params['AccountSid'])
35
+ validator = Twilio::Security::RequestValidator.new(auth_token)
36
+ signature = env['HTTP_X_TWILIO_SIGNATURE'] || ''
37
+ if validator.validate(original_url, params, signature)
38
+ @app.call(env)
39
+ else
40
+ [
41
+ 403,
42
+ { 'Content-Type' => 'text/plain' },
43
+ ['Twilio Request Validation Failed.']
44
+ ]
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "version"
4
+ require "rack/twilio_webhook_authentication"
5
+ require "twilio-ruby/security/request_validator"
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twilio
4
+ module Security
5
+ class RequestValidator
6
+ ##
7
+ # Initialize a Request Validator. auth_token will either be grabbed from the global Twilio object or you can
8
+ # pass it in here.
9
+ #
10
+ # @param [String] auth_token Your account auth token, used to sign requests
11
+ def initialize(auth_token = nil)
12
+ @auth_token = auth_token || Twilio.auth_token
13
+ raise ArgumentError, 'Auth token is required' if @auth_token.nil?
14
+ end
15
+
16
+ ##
17
+ # Validates that after hashing a request with Twilio's request-signing algorithm
18
+ # (https://www.twilio.com/docs/usage/security#validating-requests), the hash matches the signature parameter
19
+ #
20
+ # @param [String] url The url sent to your server, including any query parameters
21
+ # @param [String, Hash, #to_unsafe_h] params In most cases, this is the POST parameters as a hash. If you received
22
+ # a bodySHA256 parameter in the query string, this parameter can instead be the POST body as a string to
23
+ # validate JSON or other text-based payloads that aren't x-www-form-urlencoded.
24
+ # @param [String] signature The expected signature, from the X-Twilio-Signature header of the request
25
+ #
26
+ # @return [Boolean] whether or not the computed signature matches the signature parameter
27
+ def validate(url, params, signature)
28
+ params_hash = body_or_hash(params)
29
+ if params_hash.is_a? Enumerable
30
+ expected = build_signature_for(url, params_hash)
31
+ secure_compare(expected, signature)
32
+ else
33
+ expected_signature = build_signature_for(url, {})
34
+ body_hash = URI.decode_www_form(URI(url).query).to_h['bodySHA256']
35
+ expected_hash = build_hash_for(params)
36
+ secure_compare(expected_signature, signature) && secure_compare(expected_hash, body_hash)
37
+ end
38
+ end
39
+
40
+ ##
41
+ # Build a SHA256 hash for a body string
42
+ #
43
+ # @param [String] body String to hash
44
+ #
45
+ # @return [String] A hex-encoded SHA256 of the body string
46
+ def build_hash_for(body)
47
+ hasher = OpenSSL::Digest.new('sha256')
48
+ hasher.hexdigest(body)
49
+ end
50
+
51
+ ##
52
+ # Build a SHA1-HMAC signature for a url and parameter hash
53
+ #
54
+ # @param [String] url The request url, including any query parameters
55
+ # @param [#join] params The POST parameters
56
+ #
57
+ # @return [String] A base64 encoded SHA1-HMAC
58
+ def build_signature_for(url, params)
59
+ data = url + params.sort.join
60
+ digest = OpenSSL::Digest.new('sha1')
61
+ Base64.strict_encode64(OpenSSL::HMAC.digest(digest, @auth_token, data))
62
+ end
63
+
64
+ private
65
+
66
+ # Compares two strings in constant time to avoid timing attacks.
67
+ # Borrowed from ActiveSupport::MessageVerifier.
68
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/message_verifier.rb
69
+ def secure_compare(a, b)
70
+ return false unless a.bytesize == b.bytesize
71
+
72
+ l = a.unpack("C#{a.bytesize}")
73
+
74
+ res = 0
75
+ b.each_byte { |byte| res |= byte ^ l.shift }
76
+ res.zero?
77
+ end
78
+
79
+ # `ActionController::Parameters` no longer, as of Rails 5, inherits
80
+ # from `Hash` so the `sort` method, used above in `build_signature_for`
81
+ # is deprecated.
82
+ #
83
+ # `to_unsafe_h` was introduced in Rails 4.2.1, before then it is still
84
+ # possible to sort on an ActionController::Parameters object.
85
+ #
86
+ # We use `to_unsafe_h` as `to_h` returns a hash of the permitted
87
+ # parameters only and we need all the parameters to create the signature.
88
+ def body_or_hash(params_or_body)
89
+ if params_or_body.respond_to?(:to_unsafe_h)
90
+ params_or_body.to_unsafe_h
91
+ else
92
+ params_or_body
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TwilioRubyAuthenticateWebhooks
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "twilio-ruby-authenticate-webhooks"
9
+ spec.version = TwilioRubyAuthenticateWebhooks::VERSION
10
+ spec.authors = ["Erik Griffin"]
11
+ spec.email = ["erik@pathrise.com"]
12
+
13
+ spec.summary = "Authenticate Twilio Webhook Requests"
14
+ spec.description = "Uses Rack Middleware to authenticate Twilio Webhook requests in your app."
15
+ spec.homepage = "https://github.com/pathrise-eng/twilio-ruby-authenticate-webhooks"
16
+ spec.license = "MIT"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/pathrise-eng/twilio-ruby-authenticate-webhooks"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 2.0"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twilio-ruby-authenticate-webhooks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Erik Griffin
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-10-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Uses Rack Middleware to authenticate Twilio Webhook requests in your
42
+ app.
43
+ email:
44
+ - erik@pathrise.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - bin/console
55
+ - bin/setup
56
+ - lib/rack/twilio_webhook_authentication.rb
57
+ - lib/twilio-ruby-authenticate-webhooks.rb
58
+ - lib/twilio-ruby/security/request_validator.rb
59
+ - lib/version.rb
60
+ - twilio-ruby-authenticate-webhooks.gemspec
61
+ homepage: https://github.com/pathrise-eng/twilio-ruby-authenticate-webhooks
62
+ licenses:
63
+ - MIT
64
+ metadata:
65
+ homepage_uri: https://github.com/pathrise-eng/twilio-ruby-authenticate-webhooks
66
+ source_code_uri: https://github.com/pathrise-eng/twilio-ruby-authenticate-webhooks
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubygems_version: 3.0.3
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Authenticate Twilio Webhook Requests
86
+ test_files: []