intercom-rails 1.0.1 → 1.1.1

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: e2fc85ba00358eada296192f07e67ba84f37424ba7a5d8ee9ea5f83e5a0bc60b
4
- data.tar.gz: e3f4bfc29ba15e72c9f19c3c363abeb587ff6668637740edee063c69a329d648
3
+ metadata.gz: ff14436633adc07b04ea0fff8507abf54e6fd34e4c4ab943c3a26aa691d69801
4
+ data.tar.gz: 7fe20bd0ce9cff4efe03a935886b43ac8bed8f56366e75f1733a5948f7145299
5
5
  SHA512:
6
- metadata.gz: eb3a484b71432a961158d13ee67bc43e867d74e4ecfad7d81b58607e7f09aeacb6f9e002df29b3028ac3e71717cece13a2251a0d7a5636ba67264c59465a5167
7
- data.tar.gz: 689566c0e89a88620c018c41e8c49ff23e5aeedc32c180f0684e94d27b2112ba0ce13d5c691a89fa7db0a492352962e41be6e831a8855536fac5c6cfb557d782
6
+ metadata.gz: 744323aa7316eebe9923ef4a15897e6cf0ee24fda86e42f19ee865f1050e09840a4921be0b8f706a4fd610d66d584f4155a83a7483686949ec46e858ca2a9623
7
+ data.tar.gz: a93f75e039d873083243e3c847b8ccc4e2f35e812c4e05d5423fca83c36419aa908348003770133384044629584e63d4f55feafa6ff6b9f68e9d4b172274e2e0
data/README.md CHANGED
@@ -69,6 +69,53 @@ It is possible to enable Identity Verification for the Intercom Messenger and yo
69
69
  ```
70
70
  **Note: This example is just for the sake of simplicity, you should never include this secret in source control. Instead, you should use the Rails [secret config](http://guides.rubyonrails.org/4_1_release_notes.html#config-secrets-yml) feature.**
71
71
 
72
+ ### JWT Authentication
73
+ You can enable JWT authentication for enhanced security with the Intercom Messenger. This feature uses JSON Web Tokens (JWTs) to authenticate users instead of the traditional user_hash method. To enable JWT authentication, add the following to your `config/initializers/intercom.rb`:
74
+
75
+ ```ruby
76
+ config.jwt.enabled = true
77
+ ```
78
+
79
+ #### JWT Expiry
80
+ You can set an expiry time for JWTs. This determines how long the token remains valid:
81
+
82
+ ```ruby
83
+ config.jwt.expiry = 12.hours # Token expires after 12 hours
84
+ ```
85
+
86
+ If no expiry is set, the JWT will not include an expiration claim.
87
+
88
+ #### Signed User Fields
89
+ You can specify which user fields should be included in the JWT payload and removed from the client-side settings for enhanced security:
90
+
91
+ ```ruby
92
+ config.jwt.signed_user_fields = [:email, :name, :plan, :team_id]
93
+ ```
94
+
95
+ With this configuration, these fields will be:
96
+ - Included in the signed JWT payload
97
+ - Removed from the client-side `intercomSettings` object
98
+ - Still available to Intercom through the secure JWT
99
+
100
+ #### Per-Request JWT Configuration
101
+ You can also configure JWT settings on a per-request basis using the `intercom_script_tag` helper:
102
+
103
+ ```erb
104
+ <%= intercom_script_tag({
105
+ :user_id => current_user.id,
106
+ :email => current_user.email
107
+ }, {
108
+ :jwt_enabled => true,
109
+ :jwt_expiry => 1.hour
110
+ }) %>
111
+ ```
112
+
113
+ **Important Notes:**
114
+ - JWT authentication requires an `api_secret` to be configured
115
+ - JWT is only generated when a `user_id` is present
116
+ - When JWT is enabled, the `user_id` is removed from client-side settings and only included in the secure JWT
117
+ - Other configured signed fields are also removed from client-side settings when JWT is used
118
+
72
119
  ### Shutdown
73
120
  We make use of first-party cookies so that we can identify your users the next time they open your messenger. When people share devices with someone else, they might be able to see the most recently logged in user’s conversation history until the cookie expires. Because of this, it’s very important to properly shutdown Intercom when a user’s session on your app ends (either manually or due to an automated logout).
74
121
 
@@ -342,7 +389,7 @@ CSP support for automatic insertion exposes two namespaces that can be defined b
342
389
  - String CoreExtensions::IntercomRails::AutoInclude.csp_nonce_hook(controller)
343
390
  - nil CoreExtensions::IntercomRails::AutoInclude.csp_sha256_hook(controller, SHA-256 whitelist entry)
344
391
 
345
- For instance, a CSP nonce can be inserted using the [Twitter Secure Headers](https://github.com/twitter/secureheaders) gem with the following code:
392
+ For instance, a CSP nonce can be inserted using the [Github Secure Headers](https://github.com/github/secure_headers) gem with the following code:
346
393
  ```ruby
347
394
  module CoreExtensions
348
395
  module IntercomRails
@@ -143,6 +143,16 @@ module IntercomRails
143
143
  end
144
144
  end
145
145
 
146
+ config_group :jwt do
147
+ config_accessor :enabled
148
+ config_accessor :expiry
149
+ config_accessor :signed_user_fields do |value|
150
+ unless value.nil? || (value.kind_of?(Array) && value.all? { |v| v.kind_of?(Symbol) || v.kind_of?(String) })
151
+ raise ArgumentError, "jwt.signed_user_fields must be an array of symbols or strings"
152
+ end
153
+ end
154
+ end
155
+
146
156
  end
147
157
 
148
158
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'active_support/all'
4
4
  require 'action_view'
5
+ require 'jwt'
5
6
 
6
7
  module IntercomRails
7
8
 
@@ -17,7 +18,7 @@ module IntercomRails
17
18
  include ::ActionView::Helpers::TagHelper
18
19
 
19
20
  attr_reader :user_details, :company_details, :show_everywhere, :session_duration
20
- attr_accessor :secret, :widget_options, :controller, :nonce, :encrypted_mode_enabled, :encrypted_mode
21
+ attr_accessor :secret, :widget_options, :controller, :nonce, :encrypted_mode_enabled, :encrypted_mode, :jwt_enabled, :jwt_expiry
21
22
 
22
23
  def initialize(options = {})
23
24
  self.secret = options[:secret] || Config.api_secret
@@ -25,14 +26,22 @@ module IntercomRails
25
26
  self.controller = options[:controller]
26
27
  @show_everywhere = options[:show_everywhere]
27
28
  @session_duration = session_duration_from_config
28
- self.user_details = options[:find_current_user_details] ? find_current_user_details : options[:user_details]
29
+ self.jwt_enabled = options[:jwt_enabled] || Config.jwt.enabled
30
+ self.jwt_expiry = options[:jwt_expiry] || Config.jwt.expiry
31
+
32
+ initial_user_details = if options[:find_current_user_details]
33
+ find_current_user_details
34
+ else
35
+ options[:user_details] || {}
36
+ end
37
+
38
+ lead_attributes = find_lead_attributes
39
+
40
+ self.user_details = initial_user_details.merge(lead_attributes)
29
41
 
30
42
  self.encrypted_mode_enabled = options[:encrypted_mode] || Config.encrypted_mode
31
43
  self.encrypted_mode = IntercomRails::EncryptedMode.new(secret, options[:initialization_vector], {:enabled => encrypted_mode_enabled})
32
44
 
33
- # Request specific custom data for non-signed up users base on lead_attributes
34
- self.user_details = self.user_details.merge(find_lead_attributes)
35
-
36
45
  self.company_details = if options[:find_current_company_details]
37
46
  find_current_company_details
38
47
  elsif options[:user_details]
@@ -45,7 +54,7 @@ module IntercomRails
45
54
  return false if user_details[:excluded_user] == true
46
55
  valid = user_details[:app_id].present?
47
56
  unless @show_everywhere
48
- valid = valid && (user_details[:user_id] || user_details[:email]).present?
57
+ valid = valid && @has_identity
49
58
  end
50
59
  if nonce
51
60
  valid = valid && valid_nonce?
@@ -113,12 +122,45 @@ module IntercomRails
113
122
  "window.intercomSettings = #{plaintext_javascript};#{intercom_encrypted_payload_javascript}(function(){var w=window;var ic=w.Intercom;if(typeof ic===\"function\"){ic('update',intercomSettings);}else{var d=document;var i=function(){i.c(arguments)};i.q=[];i.c=function(args){i.q.push(args)};w.Intercom=i;function l(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='#{Config.library_url || "https://widget.intercom.io/widget/#{j app_id}"}';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}};})()"
114
123
  end
115
124
 
125
+ def generate_jwt
126
+ return nil unless user_details[:user_id].present?
127
+
128
+ payload = { user_id: user_details[:user_id].to_s }
129
+
130
+ if jwt_expiry
131
+ payload[:exp] = jwt_expiry.from_now.to_i
132
+ end
133
+
134
+ if Config.jwt.signed_user_fields.present?
135
+ Config.jwt.signed_user_fields.each do |field|
136
+ field = field.to_sym
137
+ payload[field] = user_details[field].to_s if user_details[field].present?
138
+ end
139
+ end
140
+
141
+ JWT.encode(payload, secret, 'HS256')
142
+ end
143
+
116
144
  def user_details=(user_details)
117
145
  @user_details = DateHelper.convert_dates_to_unix_timestamps(user_details || {})
118
146
  @user_details = @user_details.with_indifferent_access.tap do |u|
119
147
  [:email, :name, :user_id].each { |k| u.delete(k) if u[k].nil? }
120
148
 
121
- u[:user_hash] ||= user_hash if secret.present? && (u[:user_id] || u[:email]).present?
149
+ @has_identity = (u[:user_id] || u[:email]).present?
150
+
151
+ if secret.present?
152
+ if jwt_enabled && u[:user_id].present?
153
+ u[:intercom_user_jwt] ||= generate_jwt
154
+
155
+ u.delete(:user_id)
156
+ Config.jwt.signed_user_fields&.each do |field|
157
+ u.delete(field.to_sym)
158
+ end
159
+ elsif (u[:user_id] || u[:email]).present?
160
+ u[:user_hash] ||= user_hash
161
+ end
162
+ end
163
+
122
164
  u[:app_id] ||= app_id
123
165
  end
124
166
  end
@@ -1,3 +1,3 @@
1
1
  module IntercomRails
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.1"
3
3
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: intercom-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben McRedmond
8
8
  - Ciaran Lee
9
9
  - Darragh Curran
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-04-08 00:00:00.000000000 Z
13
+ date: 2026-04-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -26,6 +26,20 @@ dependencies:
26
26
  - - ">"
27
27
  - !ruby/object:Gem::Version
28
28
  version: '4.0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: jwt
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '2.0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '2.0'
29
43
  - !ruby/object:Gem::Dependency
30
44
  name: rake
31
45
  requirement: !ruby/object:Gem::Requirement
@@ -102,28 +116,14 @@ dependencies:
102
116
  requirements:
103
117
  - - "~>"
104
118
  - !ruby/object:Gem::Version
105
- version: '2.0'
106
- type: :development
107
- prerelease: false
108
- version_requirements: !ruby/object:Gem::Requirement
109
- requirements:
110
- - - "~>"
111
- - !ruby/object:Gem::Version
112
- version: '2.0'
113
- - !ruby/object:Gem::Dependency
114
- name: thin
115
- requirement: !ruby/object:Gem::Requirement
116
- requirements:
117
- - - "~>"
118
- - !ruby/object:Gem::Version
119
- version: 1.8.0
119
+ version: '3.0'
120
120
  type: :development
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
123
123
  requirements:
124
124
  - - "~>"
125
125
  - !ruby/object:Gem::Version
126
- version: 1.8.0
126
+ version: '3.0'
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: tzinfo
129
129
  requirement: !ruby/object:Gem::Requirement
@@ -187,7 +187,7 @@ homepage: http://www.intercom.io
187
187
  licenses:
188
188
  - MIT
189
189
  metadata: {}
190
- post_install_message:
190
+ post_install_message:
191
191
  rdoc_options: []
192
192
  require_paths:
193
193
  - lib
@@ -202,8 +202,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
202
  - !ruby/object:Gem::Version
203
203
  version: '0'
204
204
  requirements: []
205
- rubygems_version: 3.4.10
206
- signing_key:
205
+ rubygems_version: 3.0.3.1
206
+ signing_key:
207
207
  specification_version: 4
208
208
  summary: Rails helper for emitting javascript script tags for Intercom
209
209
  test_files: []