Fixes #13619 ## Summary - Add `TwilioSignatureVerifyConcern` that validates the `X-Twilio-Signature` header using `Twilio::Security::RequestValidator` (already bundled via `twilio-ruby` gem) - Include the concern in `Twilio::CallbackController` and `Twilio::DeliveryStatusController` — both endpoints were previously accepting requests from any source with no authentication - Channels using API key authentication (`api_key_sid` present) skip validation with a warning log, since Twilio signs with the account auth token which isn't stored for those channels ## How it works 1. `before_action` looks up the `Channel::TwilioSms` from request params (`MessagingServiceSid` or `AccountSid` + phone number) 2. Validates the HMAC-SHA1 signature using the channel's auth token 3. Returns `403 Forbidden` if signature is invalid, missing, or channel not found 4. Handles reverse proxy URL reconstruction via `X-Forwarded-Proto` header Follows the same pattern used by `Webhooks::ShopifyController` and `Webhooks::TiktokController`. ## Test plan - [x] Valid signature → 204 No Content, job enqueued - [x] Invalid signature → 403 Forbidden, job not enqueued - [x] Missing signature header → 403 Forbidden - [x] Channel not found → 403 Forbidden - [x] API key channel → skips validation, job enqueued (with warning log) - [x] MessagingServiceSid lookup → validates and enqueues - [x] All existing Twilio service/job specs pass (99 examples, 0 failures) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Sojan Jose <sojan@pepalo.com>
40 lines
786 B
Ruby
40 lines
786 B
Ruby
class Twilio::CallbackController < ApplicationController
|
|
include TwilioSignatureVerifyConcern
|
|
|
|
def create
|
|
Webhooks::TwilioEventsJob.perform_later(permitted_params.to_unsafe_hash)
|
|
|
|
head :no_content
|
|
end
|
|
|
|
private
|
|
|
|
def permitted_params # rubocop:disable Metrics/MethodLength
|
|
params.permit(
|
|
:ApiVersion,
|
|
:SmsSid,
|
|
:From,
|
|
:ToState,
|
|
:ToZip,
|
|
:AccountSid,
|
|
:MessageSid,
|
|
:FromCountry,
|
|
:ToCity,
|
|
:FromCity,
|
|
:To,
|
|
:FromZip,
|
|
:Body,
|
|
:ToCountry,
|
|
:FromState,
|
|
*Array.new(10) { |i| :"MediaUrl#{i}" },
|
|
*Array.new(10) { |i| :"MediaContentType#{i}" },
|
|
:MessagingServiceSid,
|
|
:NumMedia,
|
|
:Latitude,
|
|
:Longitude,
|
|
:MessageType,
|
|
:ProfileName
|
|
)
|
|
end
|
|
end
|