shopify_app 22.0.1 → 22.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ea486458223aad4ec68fd15a99a340d79ae306e95b8596ca7b9f447b0ac237c
4
- data.tar.gz: 7cfc9f51c225a89a3aacf87fc6cd3724c3c69399c60588cab9ac821f32b9bde0
3
+ metadata.gz: d0b7ce0abe557e14db6582b5cefd0bde0837c6e7f380934ddf9754311e8696a5
4
+ data.tar.gz: 215ce18139c607e2282f2ecf19cbc365f2fff92012bc4ea5e04cc5d3fe33f4ec
5
5
  SHA512:
6
- metadata.gz: 6c3c7f2426c24946ab299bb2499e5065c16828fe72ff8160f7d114c96ba7ff658d032494f418d58668ff0ad2fc53288a237fa92f35950e8028aeb0cd551b9d4d
7
- data.tar.gz: e86005c3bde23478adc4444ff93274f3c6e4646f063399eb80f9165ab2a0b56765209038d49174889d2e026519296f4b44a929ce0201be5bc25c2c98470f3258
6
+ metadata.gz: 3ac07379b157096ad6d8c1817058ff2d49401d82af7309c36963b184d2e65053854d02de70cfbebd164a79fa72147a9bda14f209acbb7aadf971f31da9438a1f
7
+ data.tar.gz: ae2126091214107335550b527ec183499dd38e96bbcee3ac6a5585b287403b74a14c7a260d1929c65abbe6060f243ef64ebf62ba8adf8c8de3c7392316241e83
@@ -8,10 +8,9 @@ jobs:
8
8
 
9
9
  steps:
10
10
  - uses: actions/checkout@v3
11
- - name: Set up Ruby 3.2
11
+ - name: Set up Ruby
12
12
  uses: ruby/setup-ruby@v1
13
13
  with:
14
- ruby-version: 3.2
15
14
  bundler-cache: true
16
15
  - name: Install gems
17
16
  run: |
data/.rubocop.yml CHANGED
@@ -2,7 +2,6 @@ inherit_gem:
2
2
  rubocop-shopify: rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.7
6
5
  Exclude:
7
6
  - 'test/tmp/**/*'
8
7
  - 'vendor/bundle/**/*'
data/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  Unreleased
2
2
  ----------
3
3
 
4
+ 22.1.0 (April 9,2024)
5
+ ----------
6
+ * Extracted class - `PostAuthenticateTasks` to handle post authenticate tasks. To learn more, see [post authenticate tasks](/docs/shopify_app/authentication.md#post-authenticate-tasks). [1819](https://github.com/Shopify/shopify_app/pull/1819)
7
+ * Bumps shopify_api dependency to 14.1.0 [1826](https://github.com/Shopify/shopify_app/pull/1826)
8
+
4
9
  22.0.1 (March 12, 2024)
5
10
  ----------
6
11
  * Bumps `shopify_api` to `14.0.1` [1813](https://github.com/Shopify/shopify_app/pull/1813)
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify_app (22.0.1)
4
+ shopify_app (22.1.0)
5
5
  activeresource
6
6
  addressable (~> 2.7)
7
7
  jwt (>= 2.2.3)
8
8
  rails (> 5.2.1)
9
9
  redirect_safely (~> 1.0)
10
- shopify_api (>= 14.0.1, < 15.0)
10
+ shopify_api (>= 14.1.0, < 15.0)
11
11
  sprockets-rails (>= 2.0.0)
12
12
 
13
13
  GEM
@@ -104,7 +104,7 @@ GEM
104
104
  multi_xml (>= 0.5.2)
105
105
  i18n (1.13.0)
106
106
  concurrent-ruby (~> 1.0)
107
- json (2.6.3)
107
+ json (2.7.2)
108
108
  jwt (2.7.0)
109
109
  language_server-protocol (3.17.0.3)
110
110
  loofah (2.21.3)
@@ -140,9 +140,10 @@ GEM
140
140
  racc (~> 1.4)
141
141
  oj (3.14.3)
142
142
  openssl (3.1.0)
143
- parallel (1.23.0)
144
- parser (3.2.2.1)
143
+ parallel (1.24.0)
144
+ parser (3.3.0.5)
145
145
  ast (~> 2.4.1)
146
+ racc
146
147
  prettier_print (1.2.1)
147
148
  pry (0.14.2)
148
149
  coderay (~> 1.1)
@@ -192,20 +193,21 @@ GEM
192
193
  rb-readline (0.5.5)
193
194
  redirect_safely (1.0.0)
194
195
  activemodel
195
- regexp_parser (2.8.0)
196
- rexml (3.2.5)
197
- rubocop (1.51.0)
196
+ regexp_parser (2.9.0)
197
+ rexml (3.2.6)
198
+ rubocop (1.62.1)
198
199
  json (~> 2.3)
200
+ language_server-protocol (>= 3.17.0)
199
201
  parallel (~> 1.10)
200
- parser (>= 3.2.0.0)
202
+ parser (>= 3.3.0.2)
201
203
  rainbow (>= 2.2.2, < 4.0)
202
204
  regexp_parser (>= 1.8, < 3.0)
203
205
  rexml (>= 3.2.5, < 4.0)
204
- rubocop-ast (>= 1.28.0, < 2.0)
206
+ rubocop-ast (>= 1.31.1, < 2.0)
205
207
  ruby-progressbar (~> 1.7)
206
208
  unicode-display_width (>= 2.4.0, < 3.0)
207
- rubocop-ast (1.28.1)
208
- parser (>= 3.2.1.0)
209
+ rubocop-ast (1.31.2)
210
+ parser (>= 3.3.0.4)
209
211
  rubocop-shopify (2.13.0)
210
212
  rubocop (~> 1.50)
211
213
  ruby-lsp (0.5.1)
@@ -215,7 +217,7 @@ GEM
215
217
  ruby-progressbar (1.13.0)
216
218
  ruby2_keywords (0.0.5)
217
219
  securerandom (0.2.2)
218
- shopify_api (14.0.1)
220
+ shopify_api (14.1.0)
219
221
  activesupport
220
222
  concurrent-ruby
221
223
  hash_diff
@@ -234,16 +236,16 @@ GEM
234
236
  actionpack (>= 5.2)
235
237
  activesupport (>= 5.2)
236
238
  sprockets (>= 3.0.0)
237
- sqlite3 (1.6.3-arm64-darwin)
238
- sqlite3 (1.6.3-x86_64-darwin)
239
- sqlite3 (1.6.3-x86_64-linux)
239
+ sqlite3 (1.7.3-arm64-darwin)
240
+ sqlite3 (1.7.3-x86_64-darwin)
241
+ sqlite3 (1.7.3-x86_64-linux)
240
242
  syntax_tree (6.1.1)
241
243
  prettier_print (>= 1.2.0)
242
244
  thor (1.2.2)
243
245
  timeout (0.3.2)
244
246
  tzinfo (2.0.6)
245
247
  concurrent-ruby (~> 1.0)
246
- unicode-display_width (2.4.2)
248
+ unicode-display_width (2.5.0)
247
249
  webmock (3.18.1)
248
250
  addressable (>= 2.8.0)
249
251
  crack (>= 0.3.2)
@@ -256,6 +258,7 @@ GEM
256
258
  PLATFORMS
257
259
  arm64-darwin-21
258
260
  arm64-darwin-22
261
+ arm64-darwin-23
259
262
  x86_64-darwin-19
260
263
  x86_64-darwin-20
261
264
  x86_64-darwin-21
@@ -6,14 +6,20 @@ module ShopifyApp
6
6
 
7
7
  included do
8
8
  include ShopifyApp::Localization
9
- include ShopifyApp::LoginProtection
9
+
10
+ if ShopifyApp.configuration.use_new_embedded_auth_strategy?
11
+ include ShopifyApp::TokenExchange
12
+ around_action :activate_shopify_session
13
+ else
14
+ include ShopifyApp::LoginProtection
15
+ before_action :login_again_if_different_user_or_shop
16
+ around_action :activate_shopify_session
17
+ after_action :add_top_level_redirection_headers
18
+ end
19
+
10
20
  include ShopifyApp::CsrfProtection
11
21
  include ShopifyApp::EmbeddedApp
12
22
  include ShopifyApp::EnsureBilling
13
-
14
- before_action :login_again_if_different_user_or_shop
15
- around_action :activate_shopify_session
16
- after_action :add_top_level_redirection_headers
17
23
  end
18
24
  end
19
25
  end
@@ -16,8 +16,13 @@ module ShopifyApp
16
16
  end
17
17
 
18
18
  before_action :check_shop_domain
19
- before_action :check_shop_known
20
- before_action :validate_non_embedded_session
19
+
20
+ unless ShopifyApp.configuration.use_new_embedded_auth_strategy?
21
+ # TODO: Add support to use new embedded auth strategy here when invalid
22
+ # session token can be handled by AppBridge app reload
23
+ before_action :check_shop_known
24
+ before_action :validate_non_embedded_session
25
+ end
21
26
  end
22
27
 
23
28
  def current_shopify_domain
@@ -6,7 +6,11 @@ module ShopifyApp
6
6
  include ShopifyApp::RedirectForEmbedded
7
7
 
8
8
  included do
9
- before_action :login_on_scope_changes
9
+ # Embedded auth strategy uses Shopify managed install to ensure latest access scopes,
10
+ # This will be handled automatically through token exchange
11
+ unless ShopifyApp.configuration.use_new_embedded_auth_strategy?
12
+ before_action :login_on_scope_changes
13
+ end
10
14
  end
11
15
 
12
16
  protected
@@ -23,7 +23,16 @@ module ShopifyApp
23
23
 
24
24
  return respond_with_user_token_flow if start_user_token_flow?(api_session)
25
25
 
26
- perform_post_authenticate_jobs(api_session)
26
+ if ShopifyApp::VERSION < "23.0"
27
+ # deprecated in 23.0
28
+ if ShopifyApp.configuration.custom_post_authenticate_tasks.present?
29
+ ShopifyApp.configuration.post_authenticate_tasks.perform(api_session)
30
+ else
31
+ perform_post_authenticate_jobs(api_session)
32
+ end
33
+ else
34
+ ShopifyApp.configuration.post_authenticate_tasks.perform(api_session)
35
+ end
27
36
  redirect_to_app if check_billing(api_session)
28
37
  end
29
38
 
@@ -37,6 +37,22 @@ module ShopifyApp
37
37
  def authenticate
38
38
  return render_invalid_shop_error unless sanitized_shop_name.present?
39
39
 
40
+ if ShopifyApp.configuration.use_new_embedded_auth_strategy?
41
+ ShopifyApp::Logger.debug("Starting OAuth - Redirecting to Shopify managed install")
42
+ start_install
43
+ else
44
+ ShopifyApp::Logger.debug("Starting OAuth - Redirecting to begin auth")
45
+ start_oauth
46
+ end
47
+ end
48
+
49
+ def start_install
50
+ shop_name = sanitized_shop_name.split(".").first
51
+ install_path = "https://admin.shopify.com/store/#{shop_name}/oauth/install?client_id=#{ShopifyApp.configuration.api_key}"
52
+ redirect_to(install_path, allow_other_host: true)
53
+ end
54
+
55
+ def start_oauth
40
56
  copy_return_to_param_to_session
41
57
 
42
58
  if embedded_redirect_url?
@@ -44,16 +60,16 @@ module ShopifyApp
44
60
  if embedded_param?
45
61
  redirect_for_embedded
46
62
  else
47
- start_oauth
63
+ redirect_to_begin_oauth
48
64
  end
49
65
  elsif top_level?
50
- start_oauth
66
+ redirect_to_begin_oauth
51
67
  else
52
68
  redirect_auth_to_top_level
53
69
  end
54
70
  end
55
71
 
56
- def start_oauth
72
+ def redirect_to_begin_oauth
57
73
  callback_url = ShopifyApp.configuration.login_callback_url.gsub(%r{^/}, "")
58
74
  ShopifyApp::Logger.debug("Starting OAuth with the following callback URL: #{callback_url}")
59
75
 
data/docs/Upgrading.md CHANGED
@@ -40,6 +40,17 @@ We also recommend the use of a staging site which matches your production enviro
40
40
 
41
41
  If you do run into issues, we recommend looking at our [debugging tips.](https://github.com/Shopify/shopify_app/blob/main/docs/Troubleshooting.md#debugging-tips)
42
42
 
43
+ ## Unreleased
44
+ #### (v23.0.0) - Deprecated methods in CallbackController
45
+ The following methods from `ShopifyApp::CallbackController` have been deprecated in `v23.0.0`
46
+ - `perform_after_authenticate_job`
47
+ - `install_webhooks`
48
+ - `perform_post_authenticate_jobs`
49
+
50
+ If you have overwritten these methods in your callback controller to modify the behavior of the inherited `CallbackController`, you will need to
51
+ update your app to use configurable option `config.custom_post_authenticate_tasks` instead. See [post authenticate tasks](/docs/shopify_app/authentication.md#post-authenticate-tasks)
52
+ for more information.
53
+
43
54
  ## Upgrading to `v22.0.0`
44
55
  #### Dropped support for Ruby 2.x
45
56
  Support for Ruby 2.x has been dropped as it is no longer supported. You'll need to upgrade to 3.x.x
@@ -12,7 +12,7 @@ See [*Getting started with session token authentication*](https://shopify.dev/do
12
12
 
13
13
  * [OAuth callback](#oauth-callback)
14
14
  * [Customizing callback controller](#customizing-callback-controller)
15
- * [Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
15
+ * [Run jobs after the OAuth flow](#post-authenticate-tasks)
16
16
  * [Rotate API credentials](#rotate-api-credentials)
17
17
  * [Making authenticated API requests after authorization](#making-authenticated-api-requests-after-authorization)
18
18
 
@@ -26,15 +26,11 @@ The default callback controller [`ShopifyApp::CallbackController`](../../app/con
26
26
 
27
27
  1. Logging into the shop and resetting the session
28
28
  2. Storing the session to the `SessionRepository`
29
- 3. [Installing Webhooks](/docs/shopify_app/webhooks.md)
30
- 4. [Setting Scripttags](/docs/shopify_app/script-tags.md)
31
- 5. [Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
32
- 6. Redirecting to the return address
33
-
29
+ 3. [Post authenticate tasks](#post-authenticate-tasks)
30
+ 4. Redirecting to the return address
34
31
 
35
32
  #### Customizing callback controller
36
- If the app needs to do some extra work, it can define and configure the route to a custom callback controller.
37
- Inheriting from `ShopifyApp::CallbackController` and hook into or override any of the defined helper methods.
33
+ If you need to define a custom callback controller to handle your app's use case, you can configure the callback route to your controller.
38
34
 
39
35
  Example:
40
36
 
@@ -42,11 +38,9 @@ Example:
42
38
  ```ruby
43
39
  # web/app/controllers/my_custom_callback_controller.rb
44
40
 
45
- class MyCustomCallbackController < ShopifyApp::CallbackController
46
- private
47
-
48
- def install_webhooks(session)
49
- # My custom override/definition to install webhooks
41
+ class MyCustomCallbackController
42
+ def callback
43
+ # My custom callback logic
50
44
  end
51
45
  end
52
46
  ```
@@ -69,11 +63,39 @@ Rails.application.routes.draw do
69
63
  end
70
64
  ```
71
65
 
72
- ### Run jobs after the OAuth flow
66
+ ### Post Authenticate tasks
67
+ After authentication is complete, a few tasks are run by default by PostAuthenticateTasks:
68
+ 1. [Installing Webhooks](/docs/shopify_app/webhooks.md)
69
+ 2. [Run configured after_authenticate_job](#after_authenticate_job)
70
+
71
+ The [PostAuthenticateTasks](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/auth/post_authenticate_tasks.rb)
72
+ class is responsible for triggering the webhooks manager for webhooks registration, and enqueue jobs from [after_authenticate_job](#after_authenticate_job).
73
+
74
+ If you simply need to enqueue more jobs to run after authenticate, use [after_authenticate_job](#after_authenticate_job) to define these jobs.
75
+
76
+ If your post authentication tasks is more complex and is different than just installing webhooks and enqueuing jobs,
77
+ you can customize the post authenticate tasks by creating a new class that has a `self.perform(session)` method,
78
+ and configuring `custom_post_authenticate_tasks` in the initializer.
79
+
80
+ ```ruby
81
+ # my_custom_post_authenticate_task.rb
82
+ class MyCustomPostAuthenticateTask
83
+ def self.perform(session)
84
+ # This will be triggered after OAuth callback and token exchange completion
85
+ end
86
+ end
87
+
88
+ # config/initializers/shopify_app.rb
89
+ ShopifyApp.configure do |config|
90
+ config.custom_post_authenticate_tasks = "MyCustomPostAuthenticateTask"
91
+ end
92
+ ```
93
+
94
+ #### after_authenticate_job
73
95
 
74
96
  See [`ShopifyApp::AfterAuthenticateJob`](/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb).
75
97
 
76
- If your app needs to perform specific actions after the user is authenticated successfully (i.e. every time a new session is created), ShopifyApp can queue or run a job of your choosing (note that we already provide support for automatically creating Webhooks and Scripttags). To configure the after authenticate job, update your initializer as follows:
98
+ If your app needs to perform specific actions after the user is authenticated successfully (i.e. every time a new session is created), ShopifyApp can queue or run a job of your choosing. To configure the after authenticate job, update your initializer as follows:
77
99
 
78
100
  ```ruby
79
101
  ShopifyApp.configure do |config|
@@ -18,7 +18,7 @@ ShopifyApp.configure do |config|
18
18
  end
19
19
  ```
20
20
 
21
- When the [OAuth callback](/docs/shopify_app/authentication.md#oauth-callback) is completed successfully, ShopifyApp will queue a background job which will ensure all the specified webhooks exist for that shop. Because this runs on every OAuth callback, it means your app will always have the webhooks it needs even if the user uninstalls and re-installs the app.
21
+ When the [OAuth callback](/docs/shopify_app/authentication.md#oauth-callback) or token exchange is completed successfully, ShopifyApp will queue a background job which will ensure all the specified webhooks exist for that shop. Because this runs on every OAuth callback, it means your app will always have the webhooks it needs even if the user uninstalls and re-installs the app.
22
22
 
23
23
  ShopifyApp also provides a [WebhooksController](/app/controllers/shopify_app/webhooks_controller.rb) that receives webhooks and queues a job based on the received topic. For example, if you register the webhook from above, then all you need to do is create a job called `CartsUpdateJob`. The job will be queued with 2 params: `shop_domain` and `webhook` (which is the webhook body).
24
24
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ module Auth
5
+ class PostAuthenticateTasks
6
+ class << self
7
+ def perform(session)
8
+ ShopifyApp::Logger.debug("Performing post authenticate tasks")
9
+ # Ensure we use the shop session to install webhooks
10
+ session_for_shop = session.online? ? shop_session(session) : session
11
+
12
+ install_webhooks(session_for_shop)
13
+
14
+ perform_after_authenticate_job(session)
15
+ end
16
+
17
+ private
18
+
19
+ def shop_session(session)
20
+ ShopifyApp::SessionRepository.retrieve_shop_session_by_shopify_domain(session.shop)
21
+ end
22
+
23
+ def install_webhooks(session)
24
+ ShopifyApp::Logger.debug("PostAuthenticateTasks: Installing webhooks")
25
+ return unless ShopifyApp.configuration.has_webhooks?
26
+
27
+ WebhooksManager.queue(session.shop, session.access_token)
28
+ end
29
+
30
+ def perform_after_authenticate_job(session)
31
+ ShopifyApp::Logger.debug("PostAuthenticateTasks: Performing after_authenticate_job")
32
+ config = ShopifyApp.configuration.after_authenticate_job
33
+
34
+ return unless config && config[:job].present?
35
+
36
+ job = config[:job]
37
+ job = job.constantize if job.is_a?(String)
38
+
39
+ if config[:inline] == true
40
+ job.perform_now(shop_domain: session.shop)
41
+ else
42
+ job.perform_later(shop_domain: session.shop)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -29,6 +29,9 @@ module ShopifyApp
29
29
  attr_writer :login_callback_url
30
30
  attr_accessor :embedded_redirect_url
31
31
 
32
+ # customize post authenticate tasks
33
+ attr_accessor :custom_post_authenticate_tasks
34
+
32
35
  # customise ActiveJob queue names
33
36
  attr_accessor :scripttags_manager_queue_name
34
37
  attr_accessor :webhooks_manager_queue_name
@@ -54,6 +57,8 @@ module ShopifyApp
54
57
  @scripttags_manager_queue_name = Rails.application.config.active_job.queue_name
55
58
  @webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
56
59
  @disable_webpacker = ENV["SHOPIFY_APP_DISABLE_WEBPACKER"].present?
60
+
61
+ log_callback_controller_method_deprecation
57
62
  end
58
63
 
59
64
  def login_url
@@ -126,6 +131,52 @@ module ShopifyApp
126
131
  def use_new_embedded_auth_strategy?
127
132
  wip_new_embedded_auth_strategy && embedded_app?
128
133
  end
134
+
135
+ def online_token_configured?
136
+ !ShopifyApp.configuration.user_session_repository.blank? && ShopifyApp::SessionRepository.user_storage.present?
137
+ end
138
+
139
+ def post_authenticate_tasks
140
+ @post_authenticate_tasks || begin
141
+ if custom_post_authenticate_tasks
142
+ custom_class = if custom_post_authenticate_tasks.respond_to?(:safe_constantize)
143
+ custom_post_authenticate_tasks.safe_constantize
144
+ else
145
+ custom_post_authenticate_tasks
146
+ end
147
+ end
148
+
149
+ task_class = custom_class || ShopifyApp::Auth::PostAuthenticateTasks
150
+
151
+ [
152
+ :perform,
153
+ ].each do |method|
154
+ raise(
155
+ ::ShopifyApp::ConfigurationError,
156
+ "Missing method - '#{method}' for custom_post_authenticate_tasks",
157
+ ) unless task_class.respond_to?(method)
158
+ end
159
+
160
+ task_class
161
+ end
162
+ end
163
+
164
+ private
165
+
166
+ def log_callback_controller_method_deprecation
167
+ return unless Rails.env.development?
168
+
169
+ # TODO: Remove this before releasing v23.0.0
170
+ message = <<~EOS
171
+ ================================================
172
+ => Upcoming deprecation in v23.0:
173
+ * 'CallbackController::perform_after_authenticate_job' and related methods 'install_webhooks', 'perform_after_authenticate_job'
174
+ * will be deprecated from CallbackController in the next major release. If you need to customize
175
+ * post authentication tasks, see https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/authentication.md#post-authenticate-tasks
176
+ ================================================
177
+ EOS
178
+ puts message
179
+ end
129
180
  end
130
181
 
131
182
  class BillingConfiguration
@@ -9,7 +9,7 @@ module ShopifyApp
9
9
  end
10
10
 
11
11
  def verify_proxy_request
12
- return head(:forbidden) unless query_string_valid?(request.query_string)
12
+ head(:forbidden) unless query_string_valid?(request.query_string)
13
13
  end
14
14
 
15
15
  private
@@ -263,7 +263,7 @@ module ShopifyApp
263
263
  end
264
264
 
265
265
  def online_token_configured?
266
- !ShopifyApp.configuration.user_session_repository.blank? && ShopifyApp::SessionRepository.user_storage.present?
266
+ ShopifyApp.configuration.online_token_configured?
267
267
  end
268
268
 
269
269
  def user_session_expected?
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ module TokenExchange
5
+ extend ActiveSupport::Concern
6
+
7
+ def activate_shopify_session
8
+ if current_shopify_session.blank?
9
+ retrieve_session_from_token_exchange
10
+ end
11
+
12
+ if ShopifyApp.configuration.check_session_expiry_date && current_shopify_session.expired?
13
+ @current_shopify_session = nil
14
+ retrieve_session_from_token_exchange
15
+ end
16
+
17
+ begin
18
+ ShopifyApp::Logger.debug("Activating Shopify session")
19
+ ShopifyAPI::Context.activate_session(current_shopify_session)
20
+ yield
21
+ ensure
22
+ ShopifyApp::Logger.debug("Deactivating session")
23
+ ShopifyAPI::Context.deactivate_session
24
+ end
25
+ end
26
+
27
+ def current_shopify_session
28
+ @current_shopify_session ||= begin
29
+ session_id = ShopifyAPI::Utils::SessionUtils.current_session_id(
30
+ request.headers["HTTP_AUTHORIZATION"],
31
+ nil,
32
+ online_token_configured?,
33
+ )
34
+ return nil unless session_id
35
+
36
+ ShopifyApp::SessionRepository.load_session(session_id)
37
+ end
38
+ end
39
+
40
+ def current_shopify_domain
41
+ return if params[:shop].blank?
42
+
43
+ ShopifyApp::Utils.sanitize_shop_domain(params[:shop])
44
+ end
45
+
46
+ private
47
+
48
+ def retrieve_session_from_token_exchange
49
+ # TODO: Right now JWT Middleware only updates env['jwt.shopify_domain'] from request headers tokens,
50
+ # which won't work for new installs.
51
+ # we need to update the middleware to also update the env['jwt.shopify_domain'] from the query params
52
+ domain = ShopifyApp::JWT.new(session_token).shopify_domain
53
+
54
+ ShopifyApp::Logger.info("Performing Token Exchange for [#{domain}] - (Offline)")
55
+ session = exchange_token(
56
+ shop: domain, # TODO: use jwt_shopify_domain ?
57
+ session_token: session_token,
58
+ requested_token_type: ShopifyAPI::Auth::TokenExchange::RequestedTokenType::OFFLINE_ACCESS_TOKEN,
59
+ )
60
+
61
+ if session && online_token_configured?
62
+ ShopifyApp::Logger.info("Performing Token Exchange for [#{domain}] - (Online)")
63
+ session = exchange_token(
64
+ shop: domain, # TODO: use jwt_shopify_domain ?
65
+ session_token: session_token,
66
+ requested_token_type: ShopifyAPI::Auth::TokenExchange::RequestedTokenType::ONLINE_ACCESS_TOKEN,
67
+ )
68
+ end
69
+
70
+ ShopifyApp.configuration.post_authenticate_tasks.perform(session)
71
+ end
72
+
73
+ def exchange_token(shop:, session_token:, requested_token_type:)
74
+ if session_token.blank?
75
+ # respond_to_invalid_session_token
76
+ return
77
+ end
78
+
79
+ begin
80
+ session = ShopifyAPI::Auth::TokenExchange.exchange_token(
81
+ shop: shop,
82
+ session_token: session_token,
83
+ requested_token_type: requested_token_type,
84
+ )
85
+ rescue ShopifyAPI::Errors::InvalidJwtTokenError
86
+ # respond_to_invalid_session_token
87
+ return
88
+ rescue ShopifyAPI::Errors::HttpResponseError => error
89
+ ShopifyApp::Logger.error(
90
+ "A #{error.code} error (#{error.class}) occurred during the token exchange. Response: #{error.response.body}",
91
+ )
92
+ raise
93
+ rescue => error
94
+ ShopifyApp::Logger.error("An error occurred during the token exchange: #{error.message}")
95
+ raise
96
+ end
97
+
98
+ if session
99
+ begin
100
+ ShopifyApp::SessionRepository.store_session(session)
101
+ rescue ActiveRecord::RecordNotUnique
102
+ ShopifyApp::Logger.debug("Session not stored due to concurrent token exchange calls")
103
+ end
104
+ end
105
+
106
+ session
107
+ end
108
+
109
+ def session_token
110
+ @session_token ||= id_token_header
111
+ end
112
+
113
+ def id_token_header
114
+ request.headers["HTTP_AUTHORIZATION"]&.match(/^Bearer (.+)$/)&.[](1)
115
+ end
116
+
117
+ def respond_to_invalid_session_token
118
+ # TODO: Implement this method to handle invalid session tokens
119
+
120
+ # if request.xhr?
121
+ # response.set_header("X-Shopify-Retry-Invalid-Session-Request", 1)
122
+ # unauthorized_response = { message: :unauthorized }
123
+ # render(json: { errors: [unauthorized_response] }, status: :unauthorized)
124
+ # else
125
+ # patch_session_token_url = "#{ShopifyAPI::Context.host}/patch_session_token"
126
+ # patch_session_token_params = request.query_parameters.except(:id_token)
127
+
128
+ # bounce_url = "#{ShopifyAPI::Context.host}#{request.path}?#{patch_session_token_params.to_query}"
129
+
130
+ # # App Bridge will trigger a fetch to the URL in shopify-reload, with a new session token in headers
131
+ # patch_session_token_params["shopify-reload"] = bounce_url
132
+
133
+ # redirect_to("#{patch_session_token_url}?#{patch_session_token_params.to_query}", allow_other_host: true)
134
+ # end
135
+ end
136
+
137
+ def online_token_configured?
138
+ ShopifyApp.configuration.online_token_configured?
139
+ end
140
+ end
141
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyApp
4
- VERSION = "22.0.1"
4
+ VERSION = "22.1.0"
5
5
  end
data/lib/shopify_app.rb CHANGED
@@ -52,6 +52,10 @@ module ShopifyApp
52
52
  require "shopify_app/controller_concerns/payload_verification"
53
53
  require "shopify_app/controller_concerns/app_proxy_verification"
54
54
  require "shopify_app/controller_concerns/webhook_verification"
55
+ require "shopify_app/controller_concerns/token_exchange"
56
+
57
+ # Auth helpers
58
+ require "shopify_app/auth/post_authenticate_tasks"
55
59
 
56
60
  # jobs
57
61
  require "shopify_app/jobs/webhooks_manager_job"
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shopify_app",
3
- "version": "22.0.1",
3
+ "version": "22.1.0",
4
4
  "repository": "git@github.com:Shopify/shopify_app.git",
5
5
  "author": "Shopify",
6
6
  "license": "MIT",
data/shopify_app.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_runtime_dependency("jwt", ">= 2.2.3")
20
20
  s.add_runtime_dependency("rails", "> 5.2.1")
21
21
  s.add_runtime_dependency("redirect_safely", "~> 1.0")
22
- s.add_runtime_dependency("shopify_api", ">= 14.0.1", "< 15.0")
22
+ s.add_runtime_dependency("shopify_api", ">= 14.1.0", "< 15.0")
23
23
  s.add_runtime_dependency("sprockets-rails", ">= 2.0.0")
24
24
 
25
25
  s.add_development_dependency("byebug")
data/yarn.lock CHANGED
@@ -2040,9 +2040,9 @@ flatted@^3.2.7:
2040
2040
  integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
2041
2041
 
2042
2042
  follow-redirects@^1.0.0:
2043
- version "1.15.4"
2044
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
2045
- integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==
2043
+ version "1.15.6"
2044
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
2045
+ integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
2046
2046
 
2047
2047
  fs-extra@^8.1.0:
2048
2048
  version "8.1.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_app
3
3
  version: !ruby/object:Gem::Version
4
- version: 22.0.1
4
+ version: 22.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-12 00:00:00.000000000 Z
11
+ date: 2024-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeresource
@@ -86,7 +86,7 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: 14.0.1
89
+ version: 14.1.0
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
92
  version: '15.0'
@@ -96,7 +96,7 @@ dependencies:
96
96
  requirements:
97
97
  - - ">="
98
98
  - !ruby/object:Gem::Version
99
- version: 14.0.1
99
+ version: 14.1.0
100
100
  - - "<"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '15.0'
@@ -414,6 +414,7 @@ files:
414
414
  - lib/shopify_app/access_scopes/noop_strategy.rb
415
415
  - lib/shopify_app/access_scopes/shop_strategy.rb
416
416
  - lib/shopify_app/access_scopes/user_strategy.rb
417
+ - lib/shopify_app/auth/post_authenticate_tasks.rb
417
418
  - lib/shopify_app/configuration.rb
418
419
  - lib/shopify_app/controller_concerns/app_proxy_verification.rb
419
420
  - lib/shopify_app/controller_concerns/csrf_protection.rb
@@ -425,6 +426,7 @@ files:
425
426
  - lib/shopify_app/controller_concerns/payload_verification.rb
426
427
  - lib/shopify_app/controller_concerns/redirect_for_embedded.rb
427
428
  - lib/shopify_app/controller_concerns/sanitized_params.rb
429
+ - lib/shopify_app/controller_concerns/token_exchange.rb
428
430
  - lib/shopify_app/controller_concerns/webhook_verification.rb
429
431
  - lib/shopify_app/engine.rb
430
432
  - lib/shopify_app/errors.rb
@@ -474,7 +476,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
474
476
  - !ruby/object:Gem::Version
475
477
  version: '0'
476
478
  requirements: []
477
- rubygems_version: 3.5.6
479
+ rubygems_version: 3.5.7
478
480
  signing_key:
479
481
  specification_version: 4
480
482
  summary: This gem is used to get quickly started with the Shopify API