bullet_train 1.24.0 → 1.25.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b29ed8e34efd6dd7f03bd1dd326fbe72c11f649ae444aca7d24ce569d5d6465
4
- data.tar.gz: '0947600e8b283a1ec32336db44958a59252dbf2fc046e87cd52bde0ce39f7899'
3
+ metadata.gz: 551a29522ebbd3e77672eb47a987b2fdc3962ee45244cac05b8f8b1b4390d1d3
4
+ data.tar.gz: 6b21eaa284e4128d53703ab76257357bc75dbaa1a466e8282cb3fdeee244fafb
5
5
  SHA512:
6
- metadata.gz: 3e582753d6f086561543c1c74349ddac280141a48003a458add58fa6567d3199ee381971cb63c35c7c289843fd52068c0d5a5dc921d16a8393eb781e45cebc96
7
- data.tar.gz: '09ba7e11a89b2432a7e7cff7b2cad86381815d2e199c6b40934b4979a6641e67aeecdef7802662772f8334d0c5f0d2c88cd84c5ec94c0522a32103b38ff93341'
6
+ metadata.gz: 2576dfbdfe1a290ac469855878350f7f29042fda11866e21f1dfa5967f35f681ff533616757412dfa22bc7422a66610203de88e5f4bd0a1968da975fb7519b8a
7
+ data.tar.gz: ec63a264f9f2b4e56444215013b908c73c2327216a608929233e5c14b89d92c19d18557fb71d0431db2dbe83b43bbc2f395503c869bcdc5d0cb83bb0b9bb7450
@@ -5,3 +5,125 @@ Bullet Train makes it trivial to scaffold new endpoints where external systems c
5
5
  ```
6
6
  rails generate super_scaffold:incoming_webhooks
7
7
  ```
8
+
9
+ ## Security
10
+
11
+ When receiving webhook events you need to ensure that they come from a trusted source. If the platform signs their webhook events, you can usually verify the event authenticity via their verification method. Bullet Train has webhook event signatures built in now.
12
+
13
+ ### Managing the shared secret
14
+
15
+ In order to verify signatures both sides need to have a shared secret. In the app that will be sending webhooks you'll set up an endpoint (via the UI or API) that points to the URL of the app that will be receiving them. After the endpoint is created you'll get a `webhook_secret` value. You should copy that value and set it as an `ENV` variable in the app that will be receiving the webhooks.
16
+
17
+ For purposes of this example we'll assume that the shared secret is accessible to the receiving app as `ENV["BULLET_TRAIN_WEBHOOK_SECRET"]`.
18
+
19
+ ### Bullet Train apps receiving webhooks from another Bullet Train app
20
+
21
+ In this case you can make direct use of the `BulletTrain::OutgoingWebhooks::Signature` class that comes with your Bullet Train installation.
22
+
23
+ In the app that's going to be receiving the webhooks you'd do something like this:
24
+
25
+ ```
26
+ rails generate super_scaffold:incoming_webhooks OtherBulletTrainApp
27
+ ```
28
+
29
+ Then open `app/controllers/webhooks/incoming/other_bullet_train_app_webhooks_controller.rb` and add a `before_action :verify_authenticity` call that looks like this:
30
+
31
+ ```ruby
32
+ before_action :verify_authenticity, only: [:create]
33
+
34
+ def verify_authenticity
35
+ unless BulletTrain::OutgoingWebhooks::Signature.verify_request(request, ENV["BULLET_TRAIN_WEBHOOK_SECRET"])
36
+ # Respond with an error code and message that you usually have for non-existent endpoints.
37
+ render json: {error: "Not found"}, status: :not_found
38
+ end
39
+ end
40
+ ```
41
+
42
+ ### Other Rails apps receiving webhooks from a Bullet Train app
43
+
44
+ In this case you can use rails scaffolding to generate a route and controller for handling your webhook:
45
+
46
+ ```
47
+ rails g controller BulletTrainWebhooks create
48
+ ```
49
+
50
+ You'll also want to copy Bullet Train's `BulletTrain::OutgoingWebhooks::Signature` class into your project. (Here we should link to the actual implementation in the `core` repo so that we always point to the current implementation and not one that's frozen in time.)
51
+
52
+ After adding `BulletTrain::OutgoingWebhooks::Signature` to your project you can edit `app/controllers/bullet_train_webhooks_controller.rb` to add a `before_action :verify_authenticity` call that looks like this:
53
+
54
+ ```ruby
55
+ before_action :verify_authenticity, only: [:create]
56
+
57
+ def verify_authenticity
58
+ unless BulletTrain::OutgoingWebhooks::Signature.verify_request(request, ENV["BULLET_TRAIN_WEBHOOK_SECRET"])
59
+ # Respond with an error code and message that you usually have for non-existent endpoints.`
60
+ render json: {error: "Not found"}, status: :not_found
61
+ end
62
+ end
63
+ ```
64
+
65
+ ## Other apps receiving webhooks from a Bullet Train app
66
+
67
+ Apps in other languages and frameworks will need to port the code from `bullet_train-core/bullet_train-outgoing_webhooks/lib/bullet_train/outgoing_webhooks/signature.rb` in this particular language/framework similarly to how it's described in the previous section for Rails apps. You would need to provide the verification code to your users or ask them to take the example of the `signature.rb` Ruby code. If you do so, please let us know so we can add more tried and tested examples here.
68
+
69
+ ## Other, less secure, ways to verify authenticity
70
+
71
+ Sometimes, platforms that send outgoing webhook events don't have a signature verification mechanism. In this case, you can fall back to other less secure ways of verifying that it's not a random person sending something to your endpoint that is open to the Internet when you do:
72
+
73
+ ```
74
+ rails generate super_scaffold: incoming_webhooks LessSecureApp
75
+ ```
76
+
77
+ 1. If you know the IP addresses the events will come from, you can check for them when receiving the events.
78
+ 2. Some platforms allow you to set custom headers. There you could set a secret `SecureRandom.hex` key, that you would verify when receiving the event.
79
+ 3. If you can't set custom headers, your endpoint will need to have a unique "key" in its path param or as a query param.
80
+
81
+ Here are some examples of how you would implement those less secure options if you would not have the signature verification option.
82
+
83
+ In the examples below, we will raise an error when there is an issue with the verification. This is for your error tracking. You will usually handle this error in the create action to return a message and status code that you would usually return for non-existent endpoints.
84
+
85
+ ### Verifying IP addresses
86
+
87
+ Given an env var that you populated with the expected IPs `LESS_SECURE_APP_IP_ALLOWLIST=4.311.354.505,62.9.433.116`, you could write the following code in `app/controllers/webhooks/incoming/less_secure_app_webhooks_controller.rb`:
88
+
89
+ ```ruby
90
+ before_action :verify_source_ip, only: [:create]
91
+
92
+ def verify_source_ip
93
+ source_ip = request.remote_ip
94
+ allowlist = ENV["LESS_SECURE_APP_IP_ALLOWLIST"]&.split(",")&.map(&:strip) || []
95
+
96
+ if allowlist.empty?
97
+ raise UnverifiedDomainError, "Not ready to accept LessSecureApp webhooks because no LessSecureApp IP allowlist is configured."
98
+ end
99
+
100
+ unless allowlist.include?(source_ip)
101
+ raise UnverifiedDomainError, "Webhook request from unauthorized domain"
102
+ end
103
+
104
+ true
105
+ end
106
+ ```
107
+
108
+ ### Verifying a secret
109
+
110
+ You would first need to generate a secret. You could do so by using Ruby's `SecureRandom.hex` method. Then, store the value in an environment variable like `LESS_SECURE_APP_WEBHOOK_SECRET`. Now you could have an additional before action in the `app/controllers/webhooks/incoming/less_secure_app_webhooks_controller.rb`:
111
+
112
+ ```ruby
113
+ before_action :verify_endpoint_secret_param, only: [:create]
114
+
115
+ def verify_endpoint_secret_param
116
+ expected_secret = ENV["LESS_SECURE_APP_WEBHOOK_SECRET"]
117
+ provided_secret = params[:secret]
118
+
119
+ if expected_secret.blank?
120
+ raise InvalidSecretError, "Not ready to accept ClickFunnels webhooks because no endpoint secret is configured."
121
+ end
122
+
123
+ unless provided_secret.present? && ActiveSupport::SecurityUtils.secure_compare(provided_secret, expected_secret)
124
+ raise InvalidSecretError, "Invalid webhook secret"
125
+ end
126
+
127
+ true
128
+ end
129
+ ```
@@ -1,7 +1,7 @@
1
1
  # Outgoing Webhooks
2
2
 
3
3
  ## Introduction
4
- Webhooks allow for users to be notified via HTTP request when activity takes place on their team in your application. Bullet Train applications include an entire user-facing UI that allows them not only to subscribe to webhooks, but also see a history of their attempted deliveries and debug delivery issues.
4
+ Webhooks allow for users to be notified via HTTP request when activity takes place on potentially any piece of data in your application. Bullet Train applications include an entire user-facing UI that allows them not only to create webhook endpoints, but also see a history of their attempted deliveries and debug delivery issues.
5
5
 
6
6
  ## Default Event Types
7
7
  Bullet Train can deliver webhooks for any model you've added under `Team`. We call the model a webhook is being issued for the "subject".
@@ -27,6 +27,9 @@ payment.generate_webhook(:succeeded)
27
27
  ## Delivery
28
28
  Webhooks are delivered asyncronously in a background job by default. If the resulting HTTP request results in a status code other than those in the 2XX series, it will be considered a failed attempt and delivery will be reattempted a number of times.
29
29
 
30
+ ## Security
31
+ Your outgoing webhook events come with a signature that your users can verify on the receiving end. More info in the [Security section of the Incoming Webhooks docs](./incoming.md#security)
32
+
30
33
  ## Future Plans
31
34
  - Allow users to filter webhooks to be generated by a given parent model. For example, they should be able to subscribe to `post.created`, but only for `Post` objects created within a certain `Project`.
32
35
  - Integrate [Hammerstone Refine](https://hammerstone.dev) to allow even greater configurability for filtering webhooks.
@@ -1,3 +1,3 @@
1
1
  module BulletTrain
2
- VERSION = "1.24.0"
2
+ VERSION = "1.25.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.24.0
4
+ version: 1.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver