shopify_app 22.00.0 → 22.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rubocop.yml +1 -2
- data/.rubocop.yml +0 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +20 -17
- data/app/controllers/concerns/shopify_app/ensure_has_session.rb +11 -5
- data/app/controllers/concerns/shopify_app/ensure_installed.rb +7 -2
- data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +5 -1
- data/app/controllers/shopify_app/callback_controller.rb +10 -1
- data/app/controllers/shopify_app/sessions_controller.rb +19 -3
- data/docs/Upgrading.md +11 -0
- data/docs/shopify_app/authentication.md +37 -15
- data/docs/shopify_app/webhooks.md +1 -1
- data/lib/shopify_app/auth/post_authenticate_tasks.rb +48 -0
- data/lib/shopify_app/configuration.rb +51 -0
- data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +1 -1
- data/lib/shopify_app/controller_concerns/login_protection.rb +1 -1
- data/lib/shopify_app/controller_concerns/token_exchange.rb +141 -0
- data/lib/shopify_app/version.rb +1 -1
- data/lib/shopify_app.rb +4 -0
- data/package.json +1 -1
- data/shopify_app.gemspec +1 -1
- data/yarn.lock +3 -3
- metadata +15 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0b7ce0abe557e14db6582b5cefd0bde0837c6e7f380934ddf9754311e8696a5
|
4
|
+
data.tar.gz: 215ce18139c607e2282f2ecf19cbc365f2fff92012bc4ea5e04cc5d3fe33f4ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ac07379b157096ad6d8c1817058ff2d49401d82af7309c36963b184d2e65053854d02de70cfbebd164a79fa72147a9bda14f209acbb7aadf971f31da9438a1f
|
7
|
+
data.tar.gz: ae2126091214107335550b527ec183499dd38e96bbcee3ac6a5585b287403b74a14c7a260d1929c65abbe6060f243ef64ebf62ba8adf8c8de3c7392316241e83
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,20 @@
|
|
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
|
+
|
9
|
+
22.0.1 (March 12, 2024)
|
10
|
+
----------
|
11
|
+
* Bumps `shopify_api` to `14.0.1` [1813](https://github.com/Shopify/shopify_app/pull/1813)
|
12
|
+
|
4
13
|
22.00.0 (March 5, 2024)
|
5
14
|
----------
|
15
|
+
|
16
|
+
To migrate from a previous version, please see the [v22 migration guide](docs/Upgrading.md#upgrading-to-v2200).
|
17
|
+
|
6
18
|
* ⚠️ [Breaking] Bumps minimum supported Ruby version to 3.0. Bumps `shopify_api` to 14.0 [1801](https://github.com/Shopify/shopify_app/pull/1801)
|
7
19
|
* ⚠️ [Breaking] Removes deprecated controller concerns that were renamed in `v21.10.0`. [1805](https://github.com/Shopify/shopify_app/pull/1805)
|
8
20
|
* ⚠️ [Breaking] Removes deprecated `ScripttagManager`. We realize there was communication error in our logging where we logged future deprecation instead of our inteded removal. Since we have been logging that for 2 years we felt we'd move forward with the removal instead pushing this off until the next major release. [1806](https://github.com/Shopify/shopify_app/pull/1806)
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
shopify_app (22.
|
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 (
|
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.
|
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.
|
144
|
-
parser (3.
|
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.
|
196
|
-
rexml (3.2.
|
197
|
-
rubocop (1.
|
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.
|
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.
|
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.
|
208
|
-
parser (>= 3.
|
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.
|
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.
|
238
|
-
sqlite3 (1.
|
239
|
-
sqlite3 (1.
|
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.
|
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
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
63
|
+
redirect_to_begin_oauth
|
48
64
|
end
|
49
65
|
elsif top_level?
|
50
|
-
|
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
|
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](#
|
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. [
|
30
|
-
4.
|
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
|
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
|
46
|
-
|
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
|
-
###
|
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
|
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
|
@@ -263,7 +263,7 @@ module ShopifyApp
|
|
263
263
|
end
|
264
264
|
|
265
265
|
def online_token_configured?
|
266
|
-
|
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
|
data/lib/shopify_app/version.rb
CHANGED
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
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", "
|
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.
|
2044
|
-
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.
|
2045
|
-
integrity sha512-
|
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.
|
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-
|
11
|
+
date: 2024-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activeresource
|
@@ -84,16 +84,22 @@ dependencies:
|
|
84
84
|
name: shopify_api
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 14.1.0
|
90
|
+
- - "<"
|
88
91
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
92
|
+
version: '15.0'
|
90
93
|
type: :runtime
|
91
94
|
prerelease: false
|
92
95
|
version_requirements: !ruby/object:Gem::Requirement
|
93
96
|
requirements:
|
94
|
-
- - "
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 14.1.0
|
100
|
+
- - "<"
|
95
101
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
102
|
+
version: '15.0'
|
97
103
|
- !ruby/object:Gem::Dependency
|
98
104
|
name: sprockets-rails
|
99
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -408,6 +414,7 @@ files:
|
|
408
414
|
- lib/shopify_app/access_scopes/noop_strategy.rb
|
409
415
|
- lib/shopify_app/access_scopes/shop_strategy.rb
|
410
416
|
- lib/shopify_app/access_scopes/user_strategy.rb
|
417
|
+
- lib/shopify_app/auth/post_authenticate_tasks.rb
|
411
418
|
- lib/shopify_app/configuration.rb
|
412
419
|
- lib/shopify_app/controller_concerns/app_proxy_verification.rb
|
413
420
|
- lib/shopify_app/controller_concerns/csrf_protection.rb
|
@@ -419,6 +426,7 @@ files:
|
|
419
426
|
- lib/shopify_app/controller_concerns/payload_verification.rb
|
420
427
|
- lib/shopify_app/controller_concerns/redirect_for_embedded.rb
|
421
428
|
- lib/shopify_app/controller_concerns/sanitized_params.rb
|
429
|
+
- lib/shopify_app/controller_concerns/token_exchange.rb
|
422
430
|
- lib/shopify_app/controller_concerns/webhook_verification.rb
|
423
431
|
- lib/shopify_app/engine.rb
|
424
432
|
- lib/shopify_app/errors.rb
|
@@ -468,7 +476,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
468
476
|
- !ruby/object:Gem::Version
|
469
477
|
version: '0'
|
470
478
|
requirements: []
|
471
|
-
rubygems_version: 3.5.
|
479
|
+
rubygems_version: 3.5.7
|
472
480
|
signing_key:
|
473
481
|
specification_version: 4
|
474
482
|
summary: This gem is used to get quickly started with the Shopify API
|