mirror of
https://github.com/chatwoot/chatwoot.git
synced 2026-06-04 21:02:35 +08:00
## Description * Added Meta webhook HMAC validation in meta_token_verify_concern.rb. * Wired it into instagram_controller.rb and whatsapp_controller.rb. * WhatsApp now verifies X-Hub-Signature-256 with WHATSAPP_APP_SECRET. * Instagram now verifies with either FB_APP_SECRET or INSTAGRAM_APP_SECRET. * Updated request specs so missing/invalid signatures return 401 and valid signatures still enqueue jobs. Fixes # (issue): [CW-6786](https://linear.app/chatwoot/issue/CW-6786/ghsa-7rw7-pc8v-mrr3-unauthenticated-message-injection-via-missing) ## Type of change Please delete options that are not relevant. - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? * Updated the controller specs and ran them successfully. * The original issue is no longer reproducible. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
70 lines
2.2 KiB
Ruby
70 lines
2.2 KiB
Ruby
# ref: https://github.com/jgorset/facebook-messenger#make-a-configuration-provider
|
|
class ChatwootFbProvider < Facebook::Messenger::Configuration::Providers::Base
|
|
CHANNEL_APP_SECRET_KEYS = %w[app_secret app_secret_key client_secret api_secret].freeze
|
|
|
|
def valid_verify_token?(_verify_token)
|
|
GlobalConfigService.load('FB_VERIFY_TOKEN', '')
|
|
end
|
|
|
|
def app_secret_for(page_id)
|
|
channel_app_secret_for(page_id).presence || GlobalConfigService.load('FB_APP_SECRET', '')
|
|
end
|
|
|
|
def access_token_for(page_id)
|
|
Channel::FacebookPage.where(page_id: page_id).last.page_access_token
|
|
end
|
|
|
|
private
|
|
|
|
def channel_app_secret_for(page_id)
|
|
channel = Channel::FacebookPage.where(page_id: page_id).last
|
|
return if channel.blank?
|
|
|
|
channel_app_secret_candidates(channel).first
|
|
end
|
|
|
|
def channel_app_secret_candidates(channel)
|
|
secrets = []
|
|
secrets << channel.app_secret if channel.respond_to?(:app_secret)
|
|
secrets.concat(provider_config_app_secrets(channel))
|
|
secrets.compact_blank.uniq
|
|
end
|
|
|
|
def provider_config_app_secrets(channel)
|
|
return [] unless channel.respond_to?(:provider_config)
|
|
|
|
provider_config = channel.provider_config.to_h.with_indifferent_access
|
|
CHANNEL_APP_SECRET_KEYS.filter_map { |key| provider_config[key].presence }
|
|
end
|
|
|
|
def bot
|
|
Chatwoot::Bot
|
|
end
|
|
end
|
|
|
|
Rails.application.reloader.to_prepare do
|
|
Facebook::Messenger.configure do |config|
|
|
config.provider = ChatwootFbProvider.new
|
|
end
|
|
|
|
Facebook::Messenger::Bot.on :message do |message|
|
|
Webhooks::FacebookEventsJob.perform_later(message.to_json)
|
|
end
|
|
|
|
Facebook::Messenger::Bot.on :delivery do |delivery|
|
|
Rails.logger.info "Recieved delivery status #{delivery.to_json}"
|
|
Webhooks::FacebookDeliveryJob.perform_later(delivery.to_json)
|
|
end
|
|
|
|
Facebook::Messenger::Bot.on :read do |read|
|
|
Rails.logger.info "Recieved read status #{read.to_json}"
|
|
Webhooks::FacebookDeliveryJob.perform_later(read.to_json)
|
|
end
|
|
|
|
Facebook::Messenger::Bot.on :message_echo do |message|
|
|
# Add delay to prevent race condition where echo arrives before send message API completes
|
|
# This avoids duplicate messages when echo comes early during API processing
|
|
Webhooks::FacebookEventsJob.set(wait: 2.seconds).perform_later(message.to_json)
|
|
end
|
|
end
|