shopify_app 22.0.1 → 22.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop.yml +1 -2
  3. data/.rubocop.yml +0 -1
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile.lock +20 -17
  6. data/README.md +38 -0
  7. data/app/controllers/concerns/shopify_app/ensure_has_session.rb +11 -5
  8. data/app/controllers/concerns/shopify_app/ensure_installed.rb +8 -2
  9. data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +5 -1
  10. data/app/controllers/shopify_app/callback_controller.rb +10 -1
  11. data/app/controllers/shopify_app/sessions_controller.rb +24 -4
  12. data/app/views/shopify_app/layouts/app_bridge.html.erb +17 -0
  13. data/app/views/shopify_app/sessions/patch_shopify_id_token.html.erb +0 -0
  14. data/config/routes.rb +1 -0
  15. data/docs/Troubleshooting.md +0 -23
  16. data/docs/Upgrading.md +25 -0
  17. data/docs/shopify_app/authentication.md +105 -20
  18. data/docs/shopify_app/sessions.md +110 -14
  19. data/docs/shopify_app/webhooks.md +1 -1
  20. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +2 -0
  21. data/lib/shopify_app/admin_api/with_token_refetch.rb +28 -0
  22. data/lib/shopify_app/auth/post_authenticate_tasks.rb +48 -0
  23. data/lib/shopify_app/auth/token_exchange.rb +73 -0
  24. data/lib/shopify_app/configuration.rb +54 -3
  25. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +1 -1
  26. data/lib/shopify_app/controller_concerns/embedded_app.rb +27 -0
  27. data/lib/shopify_app/controller_concerns/ensure_billing.rb +11 -3
  28. data/lib/shopify_app/controller_concerns/login_protection.rb +8 -23
  29. data/lib/shopify_app/controller_concerns/redirect_for_embedded.rb +5 -0
  30. data/lib/shopify_app/controller_concerns/token_exchange.rb +111 -0
  31. data/lib/shopify_app/controller_concerns/with_shopify_id_token.rb +41 -0
  32. data/lib/shopify_app/middleware/jwt_middleware.rb +13 -9
  33. data/lib/shopify_app/session/jwt.rb +9 -0
  34. data/lib/shopify_app/version.rb +1 -1
  35. data/lib/shopify_app.rb +9 -0
  36. data/package.json +1 -1
  37. data/shopify_app.gemspec +1 -1
  38. data/yarn.lock +3 -3
  39. metadata +12 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ea486458223aad4ec68fd15a99a340d79ae306e95b8596ca7b9f447b0ac237c
4
- data.tar.gz: 7cfc9f51c225a89a3aacf87fc6cd3724c3c69399c60588cab9ac821f32b9bde0
3
+ metadata.gz: 0e3a66e5b90c252c2d65a607911157a50a2d43ac799a70ef61bb5efaeab65013
4
+ data.tar.gz: 9c145d34b0c9a2f7f587ea2653d660ee7477f90f1348461e52c3dd84c6fc8c6a
5
5
  SHA512:
6
- metadata.gz: 6c3c7f2426c24946ab299bb2499e5065c16828fe72ff8160f7d114c96ba7ff658d032494f418d58668ff0ad2fc53288a237fa92f35950e8028aeb0cd551b9d4d
7
- data.tar.gz: e86005c3bde23478adc4444ff93274f3c6e4646f063399eb80f9165ab2a0b56765209038d49174889d2e026519296f4b44a929ce0201be5bc25c2c98470f3258
6
+ metadata.gz: ca6a9990ca94f975da2139e2e9ca4d85a9e967959e12cb08c2419d5614001b1245f292bff1916f2dfd45002a421a0a65b337148b917e4b56d6406e344c27e456
7
+ data.tar.gz: 2475faa54148b72d0764e29f101c4f6829f47d049cfcd2b800730a4fa1e6bfe35cf3e26f806366174b9e7a589e7a09143b0a4f9ef20cdbceb2e007b357bc63f3
@@ -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,36 @@
1
1
  Unreleased
2
2
  ----------
3
3
 
4
+ 22.2.0 (May 2,2024)
5
+ ----------
6
+ * Add new zero redirect authorization strategy - `Token Exchange`.
7
+ - This strategy replaces the existing OAuth flow for embedded apps and remove the redirects that were previously necessary to complete OAuth.
8
+ See ["New embedded app authorization strategy"](/README.md/#new-embedded-app-authorization-strategy) for how to enable this feature.
9
+ - Related PRs: [#1817](https://github.com/Shopify/shopify_app/pull/1817),
10
+ [#1818](https://github.com/Shopify/shopify_app/pull/1818),
11
+ [#1819](https://github.com/Shopify/shopify_app/pull/1819),
12
+ [#1821](https://github.com/Shopify/shopify_app/pull/1821),
13
+ [#1822](https://github.com/Shopify/shopify_app/pull/1822),
14
+ [#1823](https://github.com/Shopify/shopify_app/pull/1823),
15
+ [#1832](https://github.com/Shopify/shopify_app/pull/1832),
16
+ [#1833](https://github.com/Shopify/shopify_app/pull/1833),
17
+ [#1834](https://github.com/Shopify/shopify_app/pull/1834),
18
+ [#1836](https://github.com/Shopify/shopify_app/pull/1836),
19
+ * Bumps `shopify_api` to `14.3.0` [1832](https://github.com/Shopify/shopify_app/pull/1832)
20
+ * Support `id_token` from URL param [1832](https://github.com/Shopify/shopify_app/pull/1832)
21
+ * Extracted controller concern `WithShopifyIdToken`
22
+ * This concern provides a method `shopify_id_token` to retrieve the Shopify Id token from either the authorization header or the URL param `id_token`.
23
+ * `ShopifyApp::JWTMiddleware` supports retrieving session token from URL param `id_token`
24
+ * `ShopifyApp::JWTMiddleware` returns early if the app is not embedded to avoid unnecessary JWT verification
25
+ * `LoginProtection` now uses `WithShopifyIdToken` concern to retrieve the Shopify Id token, thus accepting the session token from the URL param `id_token`
26
+ * Marking `ShopifyApp::JWT` to be deprecated in version 23.0.0 [1832](https://github.com/Shopify/shopify_app/pull/1832), use `ShopifyAPI::Auth::JwtPayload` instead.
27
+ * Fix infinite redirect loop caused by handling errors from Billing API [1833](https://github.com/Shopify/shopify_app/pull/1833)
28
+
29
+ 22.1.0 (April 9,2024)
30
+ ----------
31
+ * 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)
32
+ * Bumps shopify_api dependency to 14.1.0 [1826](https://github.com/Shopify/shopify_app/pull/1826)
33
+
4
34
  22.0.1 (March 12, 2024)
5
35
  ----------
6
36
  * 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.2.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.3.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.3.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
data/README.md CHANGED
@@ -129,6 +129,44 @@ These routes are configurable. See the more detailed [*Engine*](/docs/shopify_ap
129
129
 
130
130
  To learn more about how this gem authenticates with Shopify, see [*Authentication*](/docs/shopify_app/authentication.md).
131
131
 
132
+ ### New embedded app authorization strategy (Token Exchange)
133
+
134
+ > [!TIP]
135
+ > If you are building an embedded app, we **strongly** recommend using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
136
+ > with [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) instead of the legacy authorization code grant flow.
137
+
138
+ We've introduced a new installation and authorization strategy for **embedded apps** that
139
+ eliminates the redirects that were previously necessary.
140
+ It replaces the existing [installation and authorization code grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant).
141
+
142
+ This is achieved by using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
143
+ to handle automatic app installations and scope updates, while utilizing
144
+ [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) to retrieve an access token for
145
+ authenticated API access.
146
+
147
+ ##### Enabling this new strategy in your app
148
+
149
+ 1. Enable [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
150
+ by configuring your scopes [through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration).
151
+ 2. Enable the new auth strategy in your app's ShopifyApp configuration file.
152
+
153
+ ```ruby
154
+ # config/initializers/shopify_app.rb
155
+ ShopifyApp.configure do |config|
156
+ #.....
157
+ config.embedded_app = true
158
+ config.new_embedded_auth_strategy = true
159
+
160
+ # If your app is configured to use online sessions, you can enable session expiry date check so a new access token
161
+ # is fetched automatically when the session expires.
162
+ # See expiry date check docs: https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/sessions.md#expiry-date
163
+ config.check_session_expiry_date = true
164
+ ...
165
+ end
166
+
167
+ ```
168
+ 3. Enjoy a smoother and faster app installation process.
169
+
132
170
  ### API Versioning
133
171
 
134
172
  [Shopify's API is versioned](https://shopify.dev/concepts/about-apis/versioning). With Shopify App `v1.11.0`, the included Shopify API gem allows developers to specify and update the Shopify API version they want their app or service to use. The Shopify API gem also surfaces warnings to Rails apps about [deprecated endpoints, GraphQL fields and more](https://shopify.dev/concepts/about-apis/versioning#deprecation-practices).
@@ -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,14 @@ 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
+ if ShopifyApp.configuration.use_new_embedded_auth_strategy?
21
+ include ShopifyApp::TokenExchange
22
+ around_action :activate_shopify_session
23
+ else
24
+ before_action :check_shop_known
25
+ before_action :validate_non_embedded_session
26
+ end
21
27
  end
22
28
 
23
29
  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
 
@@ -7,7 +7,7 @@ module ShopifyApp
7
7
 
8
8
  layout false, only: :new
9
9
 
10
- after_action only: [:new, :create] do |controller|
10
+ after_action only: [:new, :create, :patch_shopify_id_token] do |controller|
11
11
  controller.response.headers.except!("X-Frame-Options")
12
12
  end
13
13
 
@@ -19,6 +19,10 @@ module ShopifyApp
19
19
  authenticate
20
20
  end
21
21
 
22
+ def patch_shopify_id_token
23
+ render(layout: "shopify_app/layouts/app_bridge")
24
+ end
25
+
22
26
  def top_level_interaction
23
27
  @url = login_url_with_optional_shop(top_level: true)
24
28
  validate_shop_presence
@@ -37,6 +41,22 @@ module ShopifyApp
37
41
  def authenticate
38
42
  return render_invalid_shop_error unless sanitized_shop_name.present?
39
43
 
44
+ if ShopifyApp.configuration.use_new_embedded_auth_strategy?
45
+ ShopifyApp::Logger.debug("Starting OAuth - Redirecting to Shopify managed install")
46
+ start_install
47
+ else
48
+ ShopifyApp::Logger.debug("Starting OAuth - Redirecting to begin auth")
49
+ start_oauth
50
+ end
51
+ end
52
+
53
+ def start_install
54
+ shop_name = sanitized_shop_name.split(".").first
55
+ install_path = "https://admin.shopify.com/store/#{shop_name}/oauth/install?client_id=#{ShopifyApp.configuration.api_key}"
56
+ redirect_to(install_path, allow_other_host: true)
57
+ end
58
+
59
+ def start_oauth
40
60
  copy_return_to_param_to_session
41
61
 
42
62
  if embedded_redirect_url?
@@ -44,16 +64,16 @@ module ShopifyApp
44
64
  if embedded_param?
45
65
  redirect_for_embedded
46
66
  else
47
- start_oauth
67
+ redirect_to_begin_oauth
48
68
  end
49
69
  elsif top_level?
50
- start_oauth
70
+ redirect_to_begin_oauth
51
71
  else
52
72
  redirect_auth_to_top_level
53
73
  end
54
74
  end
55
75
 
56
- def start_oauth
76
+ def redirect_to_begin_oauth
57
77
  callback_url = ShopifyApp.configuration.login_callback_url.gsub(%r{^/}, "")
58
78
  ShopifyApp::Logger.debug("Starting OAuth with the following callback URL: #{callback_url}")
59
79
 
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title><%= ShopifyApp.configuration.application_name %></title>
6
+ <%= yield :head %>
7
+ <script
8
+ data-api-key="<%= ShopifyApp.configuration.api_key %>"
9
+ src="https://cdn.shopify.com/shopifycloud/app-bridge.js">
10
+ </script>
11
+ <%= csrf_meta_tags %>
12
+ </head>
13
+
14
+ <body>
15
+ <%= yield %>
16
+ </body>
17
+ </html>
data/config/routes.rb CHANGED
@@ -8,6 +8,7 @@ ShopifyApp::Engine.routes.draw do
8
8
  get login_url => :new, :as => :login
9
9
  post login_url => :create, :as => :authenticate
10
10
  get "logout" => :destroy, :as => :logout
11
+ get "patch_shopify_id_token" => :patch_shopify_id_token
11
12
 
12
13
  # Kept to prevent apps relying on these routes from breaking
13
14
  if login_url.gsub(%r{^/}, "") != "login"
@@ -92,29 +92,6 @@ Edit `config/initializer/shopify_app.rb` and ensure the following configurations
92
92
  + config.shop_session_repository = 'Shop'
93
93
  ```
94
94
 
95
- #### Inspect server logs
96
-
97
- If you have checked the configurations above, and the app is still using cookies, then it is possible that the `shopify_app` gem defaulted to relying on cookies. This would happen when your browser allows third-party cookies and a session token was not successfully found as part of your request.
98
-
99
- In this case, check the server logs to see if the session token was invalid:
100
-
101
- ```los
102
- [ShopifyApp::JWT] Failed to validate JWT: [JWT::<Error>] <Failure message>
103
- ```
104
-
105
- *Example*
106
-
107
- ```
108
- [ShopifyApp::JWT] Failed to validate JWT: [JWT::ImmatureSignature] Signature nbf has not been reached
109
- ```
110
-
111
- **Note:** In a local development environment, you may want to temporarily update your `Gemfile` to point to a local instance of the `shopify_app` library instad of an installed gem. This will enable you to use a debugging tool like `byebug` to debug the library.
112
-
113
- ```diff
114
- - gem 'shopify_app', '~> 14.2'
115
- + gem 'shopify_app', path: '/path/to/shopify_app'
116
- ```
117
-
118
95
  ### My app can't make requests to the Shopify API
119
96
 
120
97
  > **Note:** Session tokens cannot be used to make authenticated requests to the Shopify API. Learn more about authenticating your backend requests to Shopify APIs at [Shopify API authentication](https://shopify.dev/concepts/about-apis/authentication).
data/docs/Upgrading.md CHANGED
@@ -8,6 +8,8 @@ This file documents important changes needed to upgrade your app's Shopify App v
8
8
 
9
9
  [Unreleased](#unreleased)
10
10
 
11
+ [Upgrading to `v22.2.0`](#upgrading-to-v2220)
12
+
11
13
  [Upgrading to `v22.0.0`](#upgrading-to-v2200)
12
14
 
13
15
  [Upgrading to `v20.3.0`](#upgrading-to-v2030)
@@ -40,6 +42,29 @@ We also recommend the use of a staging site which matches your production enviro
40
42
 
41
43
  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
44
 
45
+ ## Unreleased
46
+ #### (v23.0.0) - Deprecated methods in CallbackController
47
+ The following methods from `ShopifyApp::CallbackController` have been deprecated in `v23.0.0`
48
+ - `perform_after_authenticate_job`
49
+ - `install_webhooks`
50
+ - `perform_post_authenticate_jobs`
51
+
52
+ If you have overwritten these methods in your callback controller to modify the behavior of the inherited `CallbackController`, you will need to
53
+ 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)
54
+ for more information.
55
+
56
+ #### (v23.0.0) - Deprecated "ShopifyApp::JWT" class
57
+ The `ShopifyApp::JWT` class has been deprecated in `v23.0.0`. Use [ShopifyAPI::Auth::JwtPayload](https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/auth/jwt_payload.rb)
58
+ class from the `shopify_api` gem instead. A search and replace should be enough for this migration.
59
+ - `ShopifyAPI::Auth::JwtPayload` is a superset of the `ShopifyApp::JWT` class, and contains methods that were available in `ShopifyApp::JWT`.
60
+ - `ShopifyAPI::Auth::JwtPayload` raises `ShopifyAPI::Errors::InvalidJwtTokenError` if the token is invalid.
61
+
62
+ ## Upgrading to `v22.2.0`
63
+ #### Added new feature for zero redirect embedded app authorization flow - Token Exchange
64
+ A new embedded app authorization strategy has been introduced in `v22.2.0` that eliminates the redirects that were previously necessary for OAuth.
65
+ It can replace the existing installation and authorization code grant flow.
66
+ See [new embedded app authorization strategy](./README.md#new-embedded-app-authorization-strategy) for more information.
67
+
43
68
  ## Upgrading to `v22.0.0`
44
69
  #### Dropped support for Ruby 2.x
45
70
  Support for Ruby 2.x has been dropped as it is no longer supported. You'll need to upgrade to 3.x.x
@@ -10,31 +10,83 @@ See [*Getting started with session token authentication*](https://shopify.dev/do
10
10
 
11
11
  #### Table of contents
12
12
 
13
- * [OAuth callback](#oauth-callback)
14
- * [Customizing callback controller](#customizing-callback-controller)
15
- * [Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
13
+ * [Supported types of OAuth Flow](#supported-types-of-oauth)
14
+ * [Token Exchange](#token-exchange)
15
+ * [Authorization Code Grant Flow](#authorization-code-grant-flow)
16
+ * [OAuth callback](#oauth-callback)
17
+ * [Customizing callback controller](#customizing-callback-controller)
18
+ * [Detecting scope changes](#detecting-scope-changes-1)
19
+ * [Run jobs after the OAuth flow](#post-authenticate-tasks)
16
20
  * [Rotate API credentials](#rotate-api-credentials)
17
21
  * [Making authenticated API requests after authorization](#making-authenticated-api-requests-after-authorization)
18
22
 
19
- ## OAuth callback
23
+ ## Supported types of OAuth
24
+ > [!TIP]
25
+ > If you are building an embedded app, we **strongly** recommend using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
26
+ with [token exchange](#token-exchange) instead of the authorization code grant flow.
20
27
 
21
- >️ **Note:** In Shopify App version 8.4.0, we have extracted the callback logic in its own controller. If you are upgrading from a version older than 8.4.0 the callback action and related helper methods were defined in `ShopifyApp::SessionsController` ==> you will have to extend `ShopifyApp::CallbackController` instead and port your logic to the new controller.
28
+ 1. [Token Exchange](#token-exchange)
29
+ - Recommended and is only available for embedded apps
30
+ - Doesn't require redirects, which makes authorization faster and prevents flickering when loading the app
31
+ - Access scope changes are handled by Shopify when you use [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
32
+ 2. [Authorization Code Grant Flow](#authorization-code-grant-flow)
33
+ - Suitable for non-embedded apps
34
+ - Installations, and access scope changes are managed by the app
22
35
 
23
- Upon completing the OAuth flow, Shopify calls the app at `ShopifyApp.configuration.login_callback_url`.
36
+ ## Token Exchange
37
+
38
+ OAuth process by exchanging the current user's [session token (shopify id token)](https://shopify.dev/docs/apps/auth/session-tokens) for an
39
+ [access token](https://shopify.dev/docs/apps/auth/access-token-types/online.md) to make
40
+ authenticated Shopify API queries. This will replace authorization code grant flow completely when your app is configured with [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).
41
+
42
+ To enable token exchange authorization strategy, you can follow the steps in ["New embedded app authorization strategy"](/README.md#new-embedded-app-authorization-strategy).
43
+ Upon completion of the token exchange to get the access token, [post authenticated tasks](#post-authenticate-tasks) will be run.
44
+
45
+ Learn more about:
46
+ - [How token exchange works](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange)
47
+ - [Using Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
48
+ - [Configuring access scopes through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration)
49
+
50
+ #### Handling invalid access tokens
51
+ If the access token used to make an API call is invalid, the token exchange strategy will handle the error and try to retrieve a new access token before retrying
52
+ the same operation.
53
+ See ["Re-fetching an access token when API returns Unauthorized"](/docs/shopify_app/sessions.md#re-fetching-an-access-token-when-api-returns-unauthorized) section for more information.
54
+
55
+ #### Detecting scope changes
56
+
57
+ ##### Shopify managed installation
58
+ If your access scopes are [configured through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration), scope changes will be handled by Shopify automatically.
59
+ Learn more about [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).
60
+ Using token exchange will ensure that the access token retrieved will always have the latest access scopes granted by the user.
61
+
62
+ ## Authorization Code Grant Flow
63
+ Authorization code grant flow is the OAuth flow that requires the app to redirect the user
64
+ to Shopify for installation/authorization of the app to access the shop's data. It is still required for apps that are not embedded.
65
+
66
+ To perform [authorization code grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant), you app will need to handle
67
+ [begin OAuth](#begin-oauth) and [OAuth callback](#oauth-callback) routes.
68
+
69
+ ### Begin OAuth
70
+ ShopifyApp automatically redirects the user to Shopify to complete OAuth to install the app when the `ShopifyApp.configuration.login_url` is reached.
71
+ Behind the scenes the ShopifyApp gem starts the process by calling `ShopifyAPI::Auth::Oauth.begin_auth` to build the
72
+ redirect URL with necessary parameters like the OAuth callback URL, scopes requested, type of access token (offline or online) requested, etc.
73
+ The ShopifyApp gem then redirect the merchant to Shopify, to ask for permission to install the app. (See [ShopifyApp::SessionsController.redirect_to_begin_oauth](https://github.com/Shopify/shopify_app/blob/main/app/controllers/shopify_app/sessions_controller.rb#L76-L96)
74
+ for detailed implementation)
75
+
76
+ ### OAuth callback
77
+
78
+ Shopify will redirect the merchant back to your app's callback URL once they approve the app installation.
79
+ Upon completing the OAuth flow, Shopify calls the app at `ShopifyApp.configuration.login_callback_url`. (This was provided to Shopify in the OAuth begin URL parameters)
24
80
 
25
81
  The default callback controller [`ShopifyApp::CallbackController`](../../app/controllers/shopify_app/callback_controller.rb) provides the following behaviour:
26
82
 
27
83
  1. Logging into the shop and resetting the session
28
84
  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
-
85
+ 3. [Post authenticate tasks](#post-authenticate-tasks)
86
+ 4. Redirecting to the return address
34
87
 
35
88
  #### 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.
89
+ 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
90
 
39
91
  Example:
40
92
 
@@ -42,11 +94,9 @@ Example:
42
94
  ```ruby
43
95
  # web/app/controllers/my_custom_callback_controller.rb
44
96
 
45
- class MyCustomCallbackController < ShopifyApp::CallbackController
46
- private
47
-
48
- def install_webhooks(session)
49
- # My custom override/definition to install webhooks
97
+ class MyCustomCallbackController
98
+ def callback
99
+ # My custom callback logic
50
100
  end
51
101
  end
52
102
  ```
@@ -69,11 +119,46 @@ Rails.application.routes.draw do
69
119
  end
70
120
  ```
71
121
 
72
- ### Run jobs after the OAuth flow
122
+ ### Detecting scope changes
123
+ When the OAuth process is completed, the created session has a `scope` field which holds all of the access scopes that were requested from the merchant at the time.
124
+
125
+ When an app's access scopes change, it needs to request merchants to go through OAuth again to renew its permissions.
126
+
127
+ See [Handling changes in access scopes](/docs/shopify_app/handling-access-scopes-changes.md).
128
+
129
+ ## Post Authenticate tasks
130
+ After authentication is complete, a few tasks are run by default by PostAuthenticateTasks:
131
+ 1. [Installing Webhooks](/docs/shopify_app/webhooks.md)
132
+ 2. [Run configured after_authenticate_job](#after_authenticate_job)
133
+
134
+ The [PostAuthenticateTasks](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/auth/post_authenticate_tasks.rb)
135
+ class is responsible for triggering the webhooks manager for webhooks registration, and enqueue jobs from [after_authenticate_job](#after_authenticate_job).
136
+
137
+ If you simply need to enqueue more jobs to run after authenticate, use [after_authenticate_job](#after_authenticate_job) to define these jobs.
138
+
139
+ If your post authentication tasks is more complex and is different than just installing webhooks and enqueuing jobs,
140
+ you can customize the post authenticate tasks by creating a new class that has a `self.perform(session)` method,
141
+ and configuring `custom_post_authenticate_tasks` in the initializer.
142
+
143
+ ```ruby
144
+ # my_custom_post_authenticate_task.rb
145
+ class MyCustomPostAuthenticateTask
146
+ def self.perform(session)
147
+ # This will be triggered after OAuth callback and token exchange completion
148
+ end
149
+ end
150
+
151
+ # config/initializers/shopify_app.rb
152
+ ShopifyApp.configure do |config|
153
+ config.custom_post_authenticate_tasks = "MyCustomPostAuthenticateTask"
154
+ end
155
+ ```
156
+
157
+ #### after_authenticate_job
73
158
 
74
159
  See [`ShopifyApp::AfterAuthenticateJob`](/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb).
75
160
 
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:
161
+ 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
162
 
78
163
  ```ruby
79
164
  ShopifyApp.configure do |config|