mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-04 13:07:55 +00:00 
			
		
		
		
	feat: Add support for API key authentication in Twilio (#7523)
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
		@@ -18,7 +18,11 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def authenticate_twilio
 | 
					  def authenticate_twilio
 | 
				
			||||||
    client = Twilio::REST::Client.new(permitted_params[:account_sid], permitted_params[:auth_token])
 | 
					    client = if permitted_params[:api_key_sid].present?
 | 
				
			||||||
 | 
					               Twilio::REST::Client.new(permitted_params[:api_key_sid], permitted_params[:auth_token], permitted_params[:account_sid])
 | 
				
			||||||
 | 
					             else
 | 
				
			||||||
 | 
					               Twilio::REST::Client.new(permitted_params[:account_sid], permitted_params[:auth_token])
 | 
				
			||||||
 | 
					             end
 | 
				
			||||||
    client.messages.list(limit: 1)
 | 
					    client.messages.list(limit: 1)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,6 +44,7 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
 | 
				
			|||||||
    @twilio_channel = Current.account.twilio_sms.create!(
 | 
					    @twilio_channel = Current.account.twilio_sms.create!(
 | 
				
			||||||
      account_sid: permitted_params[:account_sid],
 | 
					      account_sid: permitted_params[:account_sid],
 | 
				
			||||||
      auth_token: permitted_params[:auth_token],
 | 
					      auth_token: permitted_params[:auth_token],
 | 
				
			||||||
 | 
					      api_key_sid: permitted_params[:api_key_sid],
 | 
				
			||||||
      messaging_service_sid: permitted_params[:messaging_service_sid].presence,
 | 
					      messaging_service_sid: permitted_params[:messaging_service_sid].presence,
 | 
				
			||||||
      phone_number: phone_number,
 | 
					      phone_number: phone_number,
 | 
				
			||||||
      medium: medium
 | 
					      medium: medium
 | 
				
			||||||
@@ -52,7 +57,7 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  def permitted_params
 | 
					  def permitted_params
 | 
				
			||||||
    params.require(:twilio_channel).permit(
 | 
					    params.require(:twilio_channel).permit(
 | 
				
			||||||
      :account_id, :messaging_service_sid, :phone_number, :account_sid, :auth_token, :name, :medium
 | 
					      :account_id, :messaging_service_sid, :phone_number, :account_sid, :auth_token, :name, :medium, :api_key_sid
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,6 +111,17 @@
 | 
				
			|||||||
          "PLACEHOLDER": "Please enter your Twilio Account SID",
 | 
					          "PLACEHOLDER": "Please enter your Twilio Account SID",
 | 
				
			||||||
          "ERROR": "This field is required"
 | 
					          "ERROR": "This field is required"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "API_KEY": {
 | 
				
			||||||
 | 
					          "USE_API_KEY": "Use API Key Authentication",
 | 
				
			||||||
 | 
					          "LABEL": "API Key SID",
 | 
				
			||||||
 | 
					          "PLACEHOLDER": "Please enter your API Key SID",
 | 
				
			||||||
 | 
					          "ERROR": "This field is required"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "API_KEY_SECRET": {
 | 
				
			||||||
 | 
					          "LABEL": "API Key Secret",
 | 
				
			||||||
 | 
					          "PLACEHOLDER": "Please enter your API Key Secret",
 | 
				
			||||||
 | 
					          "ERROR": "This field is required"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "MESSAGING_SERVICE_SID": {
 | 
					        "MESSAGING_SERVICE_SID": {
 | 
				
			||||||
          "LABEL": "Messaging Service SID",
 | 
					          "LABEL": "Messaging Service SID",
 | 
				
			||||||
          "PLACEHOLDER": "Please enter your Twilio Messaging Service SID",
 | 
					          "PLACEHOLDER": "Please enter your Twilio Messaging Service SID",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,18 +81,45 @@
 | 
				
			|||||||
        }}</span>
 | 
					        }}</span>
 | 
				
			||||||
      </label>
 | 
					      </label>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="medium-8 columns messagingServiceHelptext">
 | 
				
			||||||
 | 
					      <label for="useAPIKey">
 | 
				
			||||||
 | 
					        <input
 | 
				
			||||||
 | 
					          id="useAPIKey"
 | 
				
			||||||
 | 
					          v-model="useAPIKey"
 | 
				
			||||||
 | 
					          type="checkbox"
 | 
				
			||||||
 | 
					          class="checkbox"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        {{ $t('INBOX_MGMT.ADD.TWILIO.API_KEY.USE_API_KEY') }}
 | 
				
			||||||
 | 
					      </label>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div v-if="useAPIKey" class="medium-8 columns">
 | 
				
			||||||
 | 
					      <label :class="{ error: $v.apiKeySID.$error }">
 | 
				
			||||||
 | 
					        {{ $t('INBOX_MGMT.ADD.TWILIO.API_KEY.LABEL') }}
 | 
				
			||||||
 | 
					        <input
 | 
				
			||||||
 | 
					          v-model.trim="apiKeySID"
 | 
				
			||||||
 | 
					          type="text"
 | 
				
			||||||
 | 
					          :placeholder="$t('INBOX_MGMT.ADD.TWILIO.API_KEY.PLACEHOLDER')"
 | 
				
			||||||
 | 
					          @blur="$v.apiKeySID.$touch"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <span v-if="$v.apiKeySID.$error" class="message">{{
 | 
				
			||||||
 | 
					          $t('INBOX_MGMT.ADD.TWILIO.API_KEY.ERROR')
 | 
				
			||||||
 | 
					        }}</span>
 | 
				
			||||||
 | 
					      </label>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
    <div class="medium-8 columns">
 | 
					    <div class="medium-8 columns">
 | 
				
			||||||
      <label :class="{ error: $v.authToken.$error }">
 | 
					      <label :class="{ error: $v.authToken.$error }">
 | 
				
			||||||
        {{ $t('INBOX_MGMT.ADD.TWILIO.AUTH_TOKEN.LABEL') }}
 | 
					        {{ $t(`INBOX_MGMT.ADD.TWILIO.${authTokeni18nKey}.LABEL`) }}
 | 
				
			||||||
        <input
 | 
					        <input
 | 
				
			||||||
          v-model.trim="authToken"
 | 
					          v-model.trim="authToken"
 | 
				
			||||||
          type="text"
 | 
					          type="text"
 | 
				
			||||||
          :placeholder="$t('INBOX_MGMT.ADD.TWILIO.AUTH_TOKEN.PLACEHOLDER')"
 | 
					          :placeholder="
 | 
				
			||||||
 | 
					            $t(`INBOX_MGMT.ADD.TWILIO.${authTokeni18nKey}.PLACEHOLDER`)
 | 
				
			||||||
 | 
					          "
 | 
				
			||||||
          @blur="$v.authToken.$touch"
 | 
					          @blur="$v.authToken.$touch"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <span v-if="$v.authToken.$error" class="message">{{
 | 
					        <span v-if="$v.authToken.$error" class="message">
 | 
				
			||||||
          $t('INBOX_MGMT.ADD.TWILIO.AUTH_TOKEN.ERROR')
 | 
					          {{ $t(`INBOX_MGMT.ADD.TWILIO.${authTokeni18nKey}.ERROR`) }}
 | 
				
			||||||
        }}</span>
 | 
					        </span>
 | 
				
			||||||
      </label>
 | 
					      </label>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -123,11 +150,13 @@ export default {
 | 
				
			|||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      accountSID: '',
 | 
					      accountSID: '',
 | 
				
			||||||
 | 
					      apiKeySID: '',
 | 
				
			||||||
      authToken: '',
 | 
					      authToken: '',
 | 
				
			||||||
      medium: this.type,
 | 
					      medium: this.type,
 | 
				
			||||||
      channelName: '',
 | 
					      channelName: '',
 | 
				
			||||||
      messagingServiceSID: '',
 | 
					      messagingServiceSID: '',
 | 
				
			||||||
      useMessagingService: false,
 | 
					      useMessagingService: false,
 | 
				
			||||||
 | 
					      useAPIKey: false,
 | 
				
			||||||
      phoneNumber: '',
 | 
					      phoneNumber: '',
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@@ -135,26 +164,39 @@ export default {
 | 
				
			|||||||
    ...mapGetters({
 | 
					    ...mapGetters({
 | 
				
			||||||
      uiFlags: 'inboxes/getUIFlags',
 | 
					      uiFlags: 'inboxes/getUIFlags',
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
 | 
					    authTokeni18nKey() {
 | 
				
			||||||
 | 
					      return this.useAPIKey ? 'API_KEY_SECRET' : 'AUTH_TOKEN';
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  validations() {
 | 
					  validations() {
 | 
				
			||||||
    if (this.phoneNumber) {
 | 
					    let validations = {
 | 
				
			||||||
      return {
 | 
					 | 
				
			||||||
      channelName: { required },
 | 
					      channelName: { required },
 | 
				
			||||||
        messagingServiceSID: {},
 | 
					
 | 
				
			||||||
        phoneNumber: { required, isPhoneE164OrEmpty },
 | 
					 | 
				
			||||||
      authToken: { required },
 | 
					      authToken: { required },
 | 
				
			||||||
      accountSID: { required },
 | 
					      accountSID: { required },
 | 
				
			||||||
      medium: { required },
 | 
					      medium: { required },
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    }
 | 
					    if (this.phoneNumber) {
 | 
				
			||||||
    return {
 | 
					      validations = {
 | 
				
			||||||
      channelName: { required },
 | 
					        ...validations,
 | 
				
			||||||
 | 
					        phoneNumber: { required, isPhoneE164OrEmpty },
 | 
				
			||||||
 | 
					        messagingServiceSID: {},
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      validations = {
 | 
				
			||||||
 | 
					        ...validations,
 | 
				
			||||||
        messagingServiceSID: { required },
 | 
					        messagingServiceSID: { required },
 | 
				
			||||||
        phoneNumber: {},
 | 
					        phoneNumber: {},
 | 
				
			||||||
      authToken: { required },
 | 
					 | 
				
			||||||
      accountSID: { required },
 | 
					 | 
				
			||||||
      medium: { required },
 | 
					 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.useAPIKey) {
 | 
				
			||||||
 | 
					      validations = {
 | 
				
			||||||
 | 
					        ...validations,
 | 
				
			||||||
 | 
					        apiKeySID: { required },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return validations;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    async createChannel() {
 | 
					    async createChannel() {
 | 
				
			||||||
@@ -171,6 +213,7 @@ export default {
 | 
				
			|||||||
              name: this.channelName,
 | 
					              name: this.channelName,
 | 
				
			||||||
              medium: this.medium,
 | 
					              medium: this.medium,
 | 
				
			||||||
              account_sid: this.accountSID,
 | 
					              account_sid: this.accountSID,
 | 
				
			||||||
 | 
					              api_key_sid: this.apiKeySID,
 | 
				
			||||||
              auth_token: this.authToken,
 | 
					              auth_token: this.authToken,
 | 
				
			||||||
              messaging_service_sid: this.messagingServiceSID,
 | 
					              messaging_service_sid: this.messagingServiceSID,
 | 
				
			||||||
              phone_number: this.messagingServiceSID
 | 
					              phone_number: this.messagingServiceSID
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
#  id                    :bigint           not null, primary key
 | 
					#  id                    :bigint           not null, primary key
 | 
				
			||||||
#  account_sid           :string           not null
 | 
					#  account_sid           :string           not null
 | 
				
			||||||
 | 
					#  api_key_sid           :string
 | 
				
			||||||
#  auth_token            :string           not null
 | 
					#  auth_token            :string           not null
 | 
				
			||||||
#  medium                :integer          default("sms")
 | 
					#  medium                :integer          default("sms")
 | 
				
			||||||
#  messaging_service_sid :string
 | 
					#  messaging_service_sid :string
 | 
				
			||||||
@@ -25,6 +26,7 @@ class Channel::TwilioSms < ApplicationRecord
 | 
				
			|||||||
  self.table_name = 'channel_twilio_sms'
 | 
					  self.table_name = 'channel_twilio_sms'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  validates :account_sid, presence: true
 | 
					  validates :account_sid, presence: true
 | 
				
			||||||
 | 
					  # The same parameter is used to store api_key_secret if api_key authentication is opted
 | 
				
			||||||
  validates :auth_token, presence: true
 | 
					  validates :auth_token, presence: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Must have _one_ of messaging_service_sid _or_ phone_number, and messaging_service_sid is preferred
 | 
					  # Must have _one_ of messaging_service_sid _or_ phone_number, and messaging_service_sid is preferred
 | 
				
			||||||
@@ -51,7 +53,11 @@ class Channel::TwilioSms < ApplicationRecord
 | 
				
			|||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def client
 | 
					  def client
 | 
				
			||||||
    ::Twilio::REST::Client.new(account_sid, auth_token)
 | 
					    if api_key_sid.present?
 | 
				
			||||||
 | 
					      Twilio::REST::Client.new(api_key_sid, auth_token, account_sid)
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      Twilio::REST::Client.new(account_sid, auth_token)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def send_message_from
 | 
					  def send_message_from
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					class AddApiKeySidToTwilioSms < ActiveRecord::Migration[7.0]
 | 
				
			||||||
 | 
					  def change
 | 
				
			||||||
 | 
					    add_column :channel_twilio_sms, :api_key_sid, :string
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@@ -10,7 +10,7 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
# It's strongly recommended that you check this file into your version control system.
 | 
					# It's strongly recommended that you check this file into your version control system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ActiveRecord::Schema[7.0].define(version: 2023_07_06_090122) do
 | 
					ActiveRecord::Schema[7.0].define(version: 2023_07_14_054138) do
 | 
				
			||||||
  # These are extensions that must be enabled in order to support this database
 | 
					  # These are extensions that must be enabled in order to support this database
 | 
				
			||||||
  enable_extension "pg_stat_statements"
 | 
					  enable_extension "pg_stat_statements"
 | 
				
			||||||
  enable_extension "pg_trgm"
 | 
					  enable_extension "pg_trgm"
 | 
				
			||||||
@@ -330,6 +330,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_06_090122) do
 | 
				
			|||||||
    t.datetime "updated_at", null: false
 | 
					    t.datetime "updated_at", null: false
 | 
				
			||||||
    t.integer "medium", default: 0
 | 
					    t.integer "medium", default: 0
 | 
				
			||||||
    t.string "messaging_service_sid"
 | 
					    t.string "messaging_service_sid"
 | 
				
			||||||
 | 
					    t.string "api_key_sid"
 | 
				
			||||||
    t.index ["account_sid", "phone_number"], name: "index_channel_twilio_sms_on_account_sid_and_phone_number", unique: true
 | 
					    t.index ["account_sid", "phone_number"], name: "index_channel_twilio_sms_on_account_sid_and_phone_number", unique: true
 | 
				
			||||||
    t.index ["messaging_service_sid"], name: "index_channel_twilio_sms_on_messaging_service_sid", unique: true
 | 
					    t.index ["messaging_service_sid"], name: "index_channel_twilio_sms_on_messaging_service_sid", unique: true
 | 
				
			||||||
    t.index ["phone_number"], name: "index_channel_twilio_sms_on_phone_number", unique: true
 | 
					    t.index ["phone_number"], name: "index_channel_twilio_sms_on_phone_number", unique: true
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user