diff --git a/lib/intercom-rails/config.rb b/lib/intercom-rails/config.rb index 78e761f..925e778 100644 --- a/lib/intercom-rails/config.rb +++ b/lib/intercom-rails/config.rb @@ -110,7 +110,6 @@ def self.reset! config_accessor :hide_default_launcher config_accessor :api_base config_accessor :encrypted_mode - config_accessor :jwt_enabled def self.api_key=(*) warn "Setting an Intercom API key is no longer supported; remove the `config.api_key = ...` line from config/initializers/intercom.rb" @@ -144,6 +143,15 @@ def self.company_association=(*) end end + config_group :jwt do + config_accessor :enabled + config_accessor :signed_user_fields do |value| + unless value.nil? || (value.kind_of?(Array) && value.all? { |v| v.kind_of?(Symbol) || v.kind_of?(String) }) + raise ArgumentError, "jwt.signed_user_fields must be an array of symbols or strings" + end + end + end + end end diff --git a/lib/intercom-rails/script_tag.rb b/lib/intercom-rails/script_tag.rb index cc1eea6..c482b29 100644 --- a/lib/intercom-rails/script_tag.rb +++ b/lib/intercom-rails/script_tag.rb @@ -26,7 +26,7 @@ def initialize(options = {}) self.controller = options[:controller] @show_everywhere = options[:show_everywhere] @session_duration = session_duration_from_config - self.jwt_enabled = options[:jwt_enabled] || Config.jwt_enabled + self.jwt_enabled = options[:jwt_enabled] || Config.jwt.enabled initial_user_details = if options[:find_current_user_details] find_current_user_details @@ -128,6 +128,14 @@ def generate_jwt user_id: user_details[:user_id].to_s, exp: 24.hours.from_now.to_i } + + if Config.jwt.signed_user_fields.present? + Config.jwt.signed_user_fields.each do |field| + field = field.to_sym + payload[field] = user_details[field].to_s if user_details[field].present? + end + end + JWT.encode(payload, secret, 'HS256') end @@ -139,7 +147,11 @@ def user_details=(user_details) if secret.present? if jwt_enabled && u[:user_id].present? u[:intercom_user_jwt] ||= generate_jwt - u.delete(:user_id) # No need to send plaintext user_id when using JWT + + u.delete(:user_id) + Config.jwt.signed_user_fields&.each do |field| + u.delete(field.to_sym) + end elsif (u[:user_id] || u[:email]).present? u[:user_hash] ||= user_hash end diff --git a/spec/config_spec.rb b/spec/config_spec.rb index 359e541..2f3c027 100644 --- a/spec/config_spec.rb +++ b/spec/config_spec.rb @@ -115,20 +115,48 @@ end.to output(/no longer supported/).to_stderr end - it 'gets/sets jwt_enabled' do - IntercomRails.config.jwt_enabled = true - expect(IntercomRails.config.jwt_enabled).to eq(true) - end + context 'jwt configuration' do - it 'defaults jwt_enabled to nil' do - IntercomRails.config.reset! - expect(IntercomRails.config.jwt_enabled).to eq(nil) - end + it 'gets/sets jwt_enabled' do + IntercomRails.config.jwt.enabled = true + expect(IntercomRails.config.jwt.enabled).to eq(true) + end - it 'allows jwt_enabled in block form' do - IntercomRails.config do |config| - config.jwt_enabled = true + it 'defaults jwt_enabled to nil' do + IntercomRails.config.reset! + expect(IntercomRails.config.jwt.enabled).to eq(nil) + end + + it 'allows jwt_enabled in block form' do + IntercomRails.config do |config| + config.jwt.enabled = true + end + expect(IntercomRails.config.jwt.enabled).to eq(true) + end\ + + it 'gets/sets signed_user_fields' do + IntercomRails.config.jwt.signed_user_fields = [:email, :name] + expect(IntercomRails.config.jwt.signed_user_fields).to eq([:email, :name]) + end + + it 'validates signed_user_fields is an array of symbols or strings' do + expect { + IntercomRails.config.jwt.signed_user_fields = "not_an_array" + }.to raise_error(ArgumentError) + + expect { + IntercomRails.config.jwt.signed_user_fields = [1, 2, 3] + }.to raise_error(ArgumentError) + + expect { + IntercomRails.config.jwt.signed_user_fields = [:email, "name", :custom_field] + }.not_to raise_error + end + + it 'allows nil signed_user_fields' do + expect { + IntercomRails.config.jwt.signed_user_fields = nil + }.not_to raise_error end - expect(IntercomRails.config.jwt_enabled).to eq(true) end end diff --git a/spec/script_tag_helper_spec.rb b/spec/script_tag_helper_spec.rb index 04134b2..95fabfe 100644 --- a/spec/script_tag_helper_spec.rb +++ b/spec/script_tag_helper_spec.rb @@ -59,7 +59,7 @@ end it 'enables JWT when configured' do - IntercomRails.config.jwt_enabled = true + IntercomRails.config.jwt.enabled = true output = intercom_script_tag({ user_id: '1234', email: 'test@example.com' @@ -70,7 +70,7 @@ end it 'falls back to user_hash when JWT is disabled' do - IntercomRails.config.jwt_enabled = false + IntercomRails.config.jwt.enabled = false output = intercom_script_tag({ user_id: '1234', email: 'test@example.com' diff --git a/spec/script_tag_spec.rb b/spec/script_tag_spec.rb index 0df492d..a7273fd 100644 --- a/spec/script_tag_spec.rb +++ b/spec/script_tag_spec.rb @@ -393,6 +393,80 @@ def user expect(script_tag.intercom_settings[:email]).to eq('test@example.com') expect(script_tag.intercom_settings[:name]).to eq('Test User') end + + context 'with signed_user_fields' do + before do + IntercomRails.config.jwt.signed_user_fields = [:email, :name, :plan, :team_id] + end + + it 'includes configured fields in JWT when present' do + script_tag = ScriptTag.new( + user_details: { + user_id: '1234', + email: 'test@example.com', + plan: 'pro', + team_id: 'team_123', + company_size: 100 + }, + jwt_enabled: true + ) + + jwt = script_tag.intercom_settings[:intercom_user_jwt] + decoded_payload = JWT.decode(jwt, 'super-secret', true, { algorithm: 'HS256' })[0] + + expect(decoded_payload['user_id']).to eq('1234') + expect(decoded_payload['email']).to eq('test@example.com') + expect(decoded_payload['plan']).to eq('pro') + expect(decoded_payload['team_id']).to eq('team_123') + expect(decoded_payload['company_size']).to be_nil + + expect(script_tag.intercom_settings[:user_id]).to be_nil + expect(script_tag.intercom_settings[:email]).to be_nil + expect(script_tag.intercom_settings[:plan]).to be_nil + expect(script_tag.intercom_settings[:team_id]).to be_nil + expect(script_tag.intercom_settings[:company_size]).to eq(100) + end + + it 'handles missing configured fields gracefully' do + script_tag = ScriptTag.new( + user_details: { + user_id: '1234', + email: 'test@example.com' + }, + jwt_enabled: true + ) + + jwt = script_tag.intercom_settings[:intercom_user_jwt] + decoded_payload = JWT.decode(jwt, 'super-secret', true, { algorithm: 'HS256' })[0] + + expect(decoded_payload['user_id']).to eq('1234') + expect(decoded_payload['email']).to eq('test@example.com') + expect(decoded_payload['name']).to be_nil + end + + it 'respects empty signed_user_fields configuration' do + IntercomRails.config.jwt.signed_user_fields = [] + script_tag = ScriptTag.new( + user_details: { + user_id: '1234', + email: 'test@example.com', + name: 'Test User' + }, + jwt_enabled: true + ) + + jwt = script_tag.intercom_settings[:intercom_user_jwt] + decoded_payload = JWT.decode(jwt, 'super-secret', true, { algorithm: 'HS256' })[0] + + expect(decoded_payload['user_id']).to eq('1234') + expect(decoded_payload['email']).to be_nil + expect(decoded_payload['name']).to be_nil + + + expect(script_tag.intercom_settings[:email]).to eq('test@example.com') + expect(script_tag.intercom_settings[:name]).to eq('Test User') + end + end end end