shopify_app 12.0.0 → 13.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/README.md +67 -20
  4. data/app/assets/javascripts/shopify_app/itp_helper.js +6 -6
  5. data/app/assets/javascripts/shopify_app/storage_access.js +35 -6
  6. data/app/controllers/concerns/shopify_app/authenticated.rb +1 -1
  7. data/app/controllers/shopify_app/callback_controller.rb +14 -10
  8. data/app/controllers/shopify_app/sessions_controller.rb +49 -13
  9. data/app/views/shopify_app/sessions/enable_cookies.html.erb +1 -1
  10. data/app/views/shopify_app/sessions/request_storage_access.html.erb +1 -1
  11. data/config/locales/pt-BR.yml +1 -1
  12. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +1 -5
  13. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +1 -1
  14. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
  15. data/lib/generators/shopify_app/install/templates/shopify_app.rb +1 -1
  16. data/lib/generators/shopify_app/install/templates/shopify_provider.rb +1 -1
  17. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +1 -1
  18. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -1
  19. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -1
  20. data/lib/generators/shopify_app/user_model/user_model_generator.rb +1 -1
  21. data/lib/shopify_app/configuration.rb +12 -9
  22. data/lib/shopify_app/controller_concerns/login_protection.rb +39 -22
  23. data/lib/shopify_app/engine.rb +1 -1
  24. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +18 -45
  25. data/lib/shopify_app/session/in_memory_shop_session_store.rb +4 -0
  26. data/lib/shopify_app/session/in_memory_user_session_store.rb +4 -0
  27. data/lib/shopify_app/session/session_repository.rb +38 -13
  28. data/lib/shopify_app/session/session_storage.rb +0 -10
  29. data/lib/shopify_app/session/{storage_strategies/shop_storage_strategy.rb → shop_session_storage.rb} +9 -2
  30. data/lib/shopify_app/session/{storage_strategies/user_storage_strategy.rb → user_session_storage.rb} +10 -3
  31. data/lib/shopify_app/version.rb +1 -1
  32. data/lib/shopify_app.rb +4 -2
  33. data/package-lock.json +1228 -1207
  34. data/package.json +2 -2
  35. data/shopify_app.gemspec +4 -4
  36. metadata +12 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7f75f25ebd3015036f89240acd78fb1d38bd85c6c61c361df564e0eaa1e2195
4
- data.tar.gz: 5b91f6dd61dd686a9cce74123a3b6de0498f683488204768542aa128e312f90f
3
+ metadata.gz: d4e2d37f9112725500d1a9f36fe76b743e8981920eea4838024cafd6a71cb5eb
4
+ data.tar.gz: 3c52a0ee9a7f40433ad01b82bda3a0c6d30dd9bb45c319f1d457a0bd13c30fc0
5
5
  SHA512:
6
- metadata.gz: 9119cf0bf9b9ad3a9f03c89877a7fc2337b99a98b36ec5ec01446b9a9ef15841e0bf7ba05631d15d827b5b32c535c1176f5b4b46227846ace43d27c9d55155cb
7
- data.tar.gz: c084b5f9fd03727c865621a6615e2837179f262941b2ef3c5b083515d4e23b075e0d904f9cccee29140c78d117c2624fad4ef73c883aaa0ba087d0bc816a9685
6
+ metadata.gz: 19a445a22b25b01f860a84128551234313bc44e7acd1583ec90bc96f59d65a8fac208feb1d5a6899453935dd19458196c5253605d85e3ac346be2fa405a50b4c
7
+ data.tar.gz: 526354098526753ade6d30a346ffa75b2280707638b66dba61c89b34a771e72dd4c23cfae8bda319de58a7a373e7c05d233a13a4f2f8325c7754cda1ed58d835
data/CHANGELOG.md CHANGED
@@ -1,3 +1,41 @@
1
+ 13.0.0
2
+ ------
3
+ + #887 Added concurrent user and shop session support (online/offline)
4
+ BREAKING, please see README for migration notes.
5
+
6
+ 12.0.7
7
+ ------
8
+ * Remove check for API_KEY in config that was throwing errors during install #919
9
+
10
+ 12.0.6
11
+ ------
12
+ * Adds changelog information and README updates for 8.4.0 #916
13
+
14
+ 12.0.5
15
+ ------
16
+ * Updating shopify_api gem to 9.0.1
17
+
18
+ 12.0.4
19
+ ------
20
+ * Reverts reverted PR (#895) #897
21
+
22
+ 12.0.3
23
+ ------
24
+ * Moves samesite middleware higher in the stack #898
25
+ * Fix issue where not redirecting user to granted storage page casues infinite loop #900
26
+
27
+ 12.0.2
28
+ ------
29
+ * Reverts "Fix for return_to in safari after enable_cookies/granted_storage_access" introduced in 12.0.1
30
+
31
+ 12.0.1
32
+ ------
33
+ * disable samesite cookie middleware in tests
34
+ * middleware compatibility for ruby 2.3
35
+ * samesite cookie fixes for javascript libraries
36
+ * change generators to add AppBridge instead of EASDK
37
+ * Fix for return_to in safari after enable_cookies/granted_storage_access
38
+
1
39
  12.0.0
2
40
  -----
3
41
  * Updating shopify_api gem to 9.0.0
@@ -166,6 +204,7 @@ Added support for rotating Shopify access tokens:
166
204
  8.4.0
167
205
  ----
168
206
  * Fix embedded app session management in Safari 12.1
207
+ * Note that with this change we have extracted the callback action in its own controller. If you are relying on it, see the README for more details: https://github.com/Shopify/shopify_app#callback
169
208
  * Shop names passed to OAuth are no longer case sensitive
170
209
 
171
210
  8.3.2
data/README.md CHANGED
@@ -25,6 +25,7 @@ Table of Contents
25
25
  - [AppProxyVerification](#appproxyverification)
26
26
  - [Troubleshooting](#troubleshooting)
27
27
  - [Testing an embedded app outside the Shopify admin](#testing-an-embedded-app-outside-the-shopify-admin)
28
+ - [Migration to 13.0.0](#migrating-to-13)
28
29
  - [Questions or problems?](#questions-or-problems-)
29
30
  - [Rails 6 Compatibility](#rails-6-compatibility)
30
31
  - [Upgrading from 8.6 to 9.0.0](#upgrading-from-86-to-900)
@@ -43,9 +44,9 @@ Become a Shopify App Developer
43
44
  --------------------------------
44
45
  To become a Shopify App Developer you'll need a [Shopify Partner account.](http://shopify.com/partners) If you don't have a Shopify Partner account, head to http://shopify.com/partners to create one before you start.
45
46
 
46
- Once you have a Partner account, [create a new application in the Partner Dashboard](https://help.shopify.com/en/api/tools/partner-dashboard/your-apps) to get an API key and other API credentials.
47
+ Once you have a Partner account, [create a new application in the Partner Dashboard](https://help.shopify.com/en/api/tools/partner-dashboard/your-apps) to get an API key and other API credentials.
47
48
 
48
- To create an application for development set your new app's `App URL` to the URL provided by [your tunnel](#app-tunneling), ensuring that you use `https://`. If you are not planning to embed your app inside the Shopify admin or receive webhooks, set your redirect URL to `http://localhost:3000/` and the `Whitelisted redirection URL(s)` to contain `<App URL>/auth/shopify/callback`.
49
+ To create an application for development set your new app's `App URL` to the URL provided by [your tunnel](#app-tunneling), ensuring that you use `https://`. If you are not planning to embed your app inside the Shopify admin or receive webhooks, set your redirect URL to `http://localhost:3000/` and the `Whitelisted redirection URL(s)` to contain `<App URL>/auth/shopify/callback`.
49
50
 
50
51
  Installation
51
52
  ------------
@@ -80,7 +81,7 @@ The default generator will run the `install`, `shop`, and `home_controller` gene
80
81
  $ rails generate shopify_app
81
82
  ```
82
83
 
83
- After running the generator, you will need to run `rails db:migrate` to add new tables to your database. You can start your app with `bundle exec rails server` and install your app by visiting `http://localhost` in your web browser.
84
+ After running the generator, you will need to run `rails db:migrate` to add new tables to your database. You can start your app with `bundle exec rails server` and install your app by visiting `http://localhost` in your web browser.
84
85
 
85
86
  ### API Keys
86
87
 
@@ -91,7 +92,9 @@ SHOPIFY_API_KEY=your api key
91
92
  SHOPIFY_API_SECRET=your api secret
92
93
  ```
93
94
 
94
- These values can be found on the "App Setup" page in the [Shopify Partners Dashboard][dashboard]. If you are checking your code into a code repository, ensure your `.gitignore` prevents your `.env` file from being checked into any publicly accessible code.
95
+ These values can be found on the "App Setup" page in the [Shopify Partners Dashboard][dashboard]. If you are checking your code into a code repository, ensure your `.gitignore` prevents your `.env` file from being checked into any publicly accessible code.
96
+
97
+ **You will need to load the ENV variables into your enviroment, you can do this with the [dot-env](https://github.com/bkeepers/dotenv) gem or any other method you wish to.**
95
98
 
96
99
  ### Install Generator
97
100
 
@@ -211,12 +214,21 @@ end
211
214
  Authentication
212
215
  --------------
213
216
 
217
+ ### Callback
218
+
219
+ Upon completing the authentication flow Shopify calls the app at the `callback_path` mentioned before. If the app needs to do some extra work it can define and configure the route to a custom callback controller, inheriting from `ShopifyApp::CallbackController` and hook into or override any of the defined helper methods. The default callback controller already provides the following behaviour:
220
+ * Logging into the shop and resetting the session
221
+ * [Installing Webhooks](https://github.com/Shopify/shopify_app#webhooksmanager)
222
+ * [Setting Scripttags](https://github.com/Shopify/shopify_app#scripttagsmanager)
223
+ * [Performing the AfterAuthenticate Job](https://github.com/Shopify/shopify_app#afterauthenticatejob)
224
+ * Redirecting to the return address
225
+
226
+ **Note that starting with 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.**
227
+
214
228
  ### ShopifyApp::SessionRepository
215
229
 
216
230
  `ShopifyApp::SessionRepository` allows you as a developer to define how your sessions are stored and retrieved for shops. The `SessionRepository` is configured in the `config/initializers/shopify_app.rb` file and can be set to any object that implements `self.store(auth_session, *args)` which stores the session and returns a unique identifier and `self.retrieve(id)` which returns a `ShopifyAPI::Session` for the passed id. These methods are already implemented as part of the `ShopifyApp::SessionStorage` concern, but can be overridden for custom implementation.
217
231
 
218
- If you only run the install generator then by default you will have an in memory store but it **won't work** on multi-server environments including Heroku. For multi-server environments, implement one of the following token-storage strategies.
219
-
220
232
  #### Shop-based token storage
221
233
  Storing tokens on the store model means that any user login associated to the store will have equal access levels to whatever the original user granted the app.
222
234
  ```sh
@@ -225,32 +237,35 @@ $ rails generate shopify_app:shop_model
225
237
  This will generate a shop model which will be the storage for the tokens necessary for authentication.
226
238
 
227
239
  #### User-based token storage
228
- A more granular control over level of access per user on an app might be necessary, to which the shop-based token strategy is not sufficient. Shopify supports a user-based token storage strategy where a unique token to each user can be managed.
240
+ A more granular control over level of access per user on an app might be necessary, to which the shop-based token strategy is not sufficient. Shopify supports a user-based token storage strategy where a unique token to each user can be managed. Shop tokens must still be maintained if you are running background jobs so that you can make use of them when necessary.
229
241
  ```sh
242
+ $ rails generate shopify_app:shop_model
230
243
  $ rails generate shopify_app:user_model
231
244
  ```
232
- This will generate a user model which will be the storage for the tokens necessary for authentication.
245
+ This will generate a shop model and user model which will be the storage for the tokens necessary for authentication.
233
246
 
234
247
  The current Shopify user will be stored in the rails session at `session[:shopify_user]`
235
248
 
236
- In this mode, The `self.store(auth_session, *args)` will be invoked with a Shopify User object hash, which is then used to store the token as part of a user record, rather than a store record.
237
-
238
- This will change the type of token that Shopify returns and it will only be valid for a short time. Read more about `Online access` [here](https://help.shopify.com/api/getting-started/authentication/oauth). Note that this means you won't be able to use this token to respond to Webhooks.
249
+ Read more about Online vs. Offline access [here](https://help.shopify.com/api/getting-started/authentication/oauth).
239
250
 
240
251
  #### Migrating from shop-based to user-based token strategy
241
- After running the generator, ensure that configuration settings are successfully changed:
242
-
252
+ 1. Run the `user_model` generator as mentioned above.
253
+ 2. Ensure that both your `Shop` model and `User` model includes the necessary concerns `ShopifyApp::ShopSessionStorage` and `ShopifyApp::UserSessionStorage`.
254
+ 3. Make changes to 2 initializer files as shown below:
243
255
  ```ruby
244
256
  # In the `omniauth.rb` initializer:
245
257
  provider :shopify,
246
- ShopifyApp.configuration.api_key,
247
- ShopifyApp.configuration.secret,
248
- scope: ShopifyApp.configuration.scope,
249
- per_user_permissions: true
258
+ ...
259
+ setup: lambda { |env|
260
+ ...
261
+ # Add this line
262
+ strategy.options[:per_user_permissions] = strategy.session[:user_tokens]
263
+ ...
264
+ }
250
265
 
251
266
  # In the `shopify_app.rb` initializer:
252
- config.session_repository = 'User'
253
- config.per_user_tokens = true
267
+ config.shop_session_repository = {YOUR_SHOP_MODEL_CLASS}
268
+ config.user_session_repository = {YOUR_USER_MODEL_CLASS}
254
269
  ```
255
270
 
256
271
  ### Authenticated
@@ -287,6 +302,14 @@ bin/rails g shopify_app:add_after_authenticate_job
287
302
 
288
303
  If you want to perform that action only once, e.g. send a welcome email to the user when they install the app, you should make sure that this action is idempotent, meaning that it won't have an impact if run multiple times.
289
304
 
305
+ API Versioning
306
+ --------------
307
+
308
+ Shopify's API is versioned, and you can [read about that process in the Shopify Developers documentation page](https://shopify.dev/concepts/about-apis/versioning).
309
+
310
+ Since shopify_app gem version 1.11.0, the included shopify_api gem has also been updated to allow you to easily set and switch what version of the Shopify API you want your app or service to use, as well as surface warnings to Rails apps about [deprecated endpoints, GraphQL fields and more](https://shopify.dev/concepts/about-apis/versioning#deprecation-practices).
311
+
312
+ See the [shopify_api gem README](https://github.com/Shopify/shopify_api/) for more details.
290
313
 
291
314
  WebhooksManager
292
315
  ---------------
@@ -408,7 +431,7 @@ strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
408
431
  App Tunneling
409
432
  -------------
410
433
 
411
- Your local app needs to be accessible from the public Internet in order to install it on a Shopify store, to use the [App Proxy Controller](#app-proxy-controller-generator) or receive Webhooks.
434
+ Your local app needs to be accessible from the public Internet in order to install it on a Shopify store, to use the [App Proxy Controller](#app-proxy-controller-generator) or receive Webhooks.
412
435
 
413
436
  Use a tunneling service like [ngrok](https://ngrok.com/), [Forward](https://forwardhq.com/), [Beeceptor](https://beeceptor.com/), [Mockbin](http://mockbin.org/), or [Hookbin](https://hookbin.com/) to make your development environment accessible to the internet.
414
437
 
@@ -458,11 +481,35 @@ By default, loading your embedded app will redirect to the Shopify admin, with t
458
481
  forceRedirect: <%= Rails.env.development? || Rails.env.test? ? 'false' : 'true' %>
459
482
  ```
460
483
 
484
+ Migrating to 13.0.0
485
+ -------------------
486
+
487
+ Version 13.0.0 adds the ability to use both user and shop sessions, concurrently. This however involved a large
488
+ change to how session stores work. Here are the steps to migrate to 13.x
489
+
490
+ ### Changes to `config/initializers/shopify_app.rb`
491
+ - *REMOVE* `config.per_user_tokens = [true|false]` this is no longer needed
492
+ - *CHANGE* `config.session_repository = 'Shop'` To `config.shop_session_repository = 'Shop'`
493
+ - *ADD (optional)* User Session Storage `config.user_session_repository = 'User'`
494
+
495
+ ### Shop Model Changes (normally `app/models/shop.rb`)
496
+ - *CHANGE* `include ShopifyApp::SessionStorage` to `include ShopifyApp::ShopSessionStorage`
497
+
498
+ ### Changes to `ShopifyApp::LoginProtection`
499
+ `ShopifyApp::LoginProtection`
500
+
501
+ if you are using `ShopifyApp::LoginProtection#shop_session` in your code, it will need to be
502
+ changed to `ShopifyApp::LoginProtection#activate_shopify_session`
503
+
504
+ ### Notes
505
+ You do not need a user model, a shop session is fine for most applications.
506
+
461
507
  Questions or problems?
462
508
  ----------------------
463
509
 
464
510
  - [Ask questions!](https://ecommerce.shopify.com/c/shopify-apis-and-technology)
465
511
  - [Read the docs!](https://help.shopify.com/api/guides)
512
+ - And don't forget to check the [Changelog](https://github.com/Shopify/shopify_app/blob/master/CHANGELOG.md) too!
466
513
 
467
514
  Upgrading to 11.7.0
468
515
  ---------------------------
@@ -4,31 +4,31 @@
4
4
  this.itpAction = document.getElementById('TopLevelInteractionButton');
5
5
  this.redirectUrl = opts.redirectUrl;
6
6
  }
7
-
7
+
8
8
  ITPHelper.prototype.redirect = function() {
9
9
  sessionStorage.setItem('shopify.top_level_interaction', true);
10
10
  window.location.href = this.redirectUrl;
11
11
  }
12
-
12
+
13
13
  ITPHelper.prototype.userAgentIsAffected = function() {
14
14
  return Boolean(document.hasStorageAccess);
15
15
  }
16
-
16
+
17
17
  ITPHelper.prototype.canPartitionCookies = function() {
18
18
  var versionRegEx = /Version\/12\.0\.?\d? Safari/;
19
19
  return versionRegEx.test(navigator.userAgent);
20
20
  }
21
-
21
+
22
22
  ITPHelper.prototype.setUpContent = function(onClick) {
23
23
  this.itpContent.style.display = 'block';
24
24
  this.itpAction.addEventListener('click', this.redirect.bind(this));
25
25
  }
26
-
26
+
27
27
  ITPHelper.prototype.execute = function() {
28
28
  if (!this.itpContent) {
29
29
  return;
30
30
  }
31
-
31
+
32
32
  if (this.userAgentIsAffected()) {
33
33
  this.setUpContent();
34
34
  } else {
@@ -28,18 +28,47 @@
28
28
  window.parent.location.href = this.redirectData.myshopifyUrl + '/admin/apps';
29
29
  }
30
30
 
31
- StorageAccessHelper.prototype.redirectToAppHome = function() {
32
- window.location.href = this.redirectData.appHomeUrl;
31
+ StorageAccessHelper.prototype.redirectToAppTargetUrl = function() {
32
+ window.location.href = this.redirectData.appTargetUrl;
33
+ }
34
+
35
+ StorageAccessHelper.prototype.sameSiteNoneIncompatible = function(ua) {
36
+ return ua.includes("iPhone OS 12_") || ua.includes("iPad; CPU OS 12_") || //iOS 12
37
+ (ua.includes("UCBrowser/")
38
+ ? this.isOlderUcBrowser(ua) //UC Browser < 12.13.2
39
+ : (ua.includes("Chrome/5") || ua.includes("Chrome/6"))) ||
40
+ ua.includes("Chromium/5") || ua.includes("Chromium/6") ||
41
+ (ua.includes(" OS X 10_14_") &&
42
+ ((ua.includes("Version/") && ua.includes("Safari")) || //Safari on MacOS 10.14
43
+ ua.endsWith("(KHTML, like Gecko)"))); //Web view on MacOS 10.14
44
+ }
45
+
46
+ StorageAccessHelper.prototype.isOlderUcBrowser = function(ua) {
47
+ var match = ua.match(/UCBrowser\/(\d+)\.(\d+)\.(\d+)\./);
48
+ if (!match) return false;
49
+ var major = parseInt(match[1]);
50
+ var minor = parseInt(match[2]);
51
+ var build = parseInt(match[3]);
52
+ if (major != 12) return major < 12;
53
+ if (minor != 13) return minor < 13;
54
+ return build < 2;
55
+ }
56
+
57
+ StorageAccessHelper.prototype.setCookie = function(value) {
58
+ if(!this.sameSiteNoneIncompatible(navigator.userAgent)) {
59
+ value += '; secure; SameSite=None'
60
+ }
61
+ document.cookie = value;
33
62
  }
34
63
 
35
64
  StorageAccessHelper.prototype.grantedStorageAccess = function() {
36
65
  try {
37
66
  sessionStorage.setItem('shopify.granted_storage_access', true);
38
- document.cookie = 'shopify.granted_storage_access=true';
67
+ this.setCookie('shopify.granted_storage_access=true');
39
68
  if (!document.cookie) {
40
69
  throw 'Cannot set third-party cookie.'
41
70
  }
42
- this.redirectToAppHome();
71
+ this.redirectToAppTargetUrl();
43
72
  } catch (error) {
44
73
  console.warn('Third party cookies may be blocked.', error);
45
74
  this.redirectToAppTLD(ACCESS_DENIED_STATUS);
@@ -61,7 +90,7 @@
61
90
  StorageAccessHelper.prototype.handleHasStorageAccess = function() {
62
91
  if (sessionStorage.getItem('shopify.granted_storage_access')) {
63
92
  // If app was classified by ITP and used Storage Access API to acquire access
64
- this.redirectToAppHome();
93
+ this.redirectToAppTargetUrl();
65
94
  } else {
66
95
  // If app has not been classified by ITP and still has storage access
67
96
  this.redirectToAppTLD(ACCESS_GRANTED_STATUS);
@@ -107,7 +136,7 @@
107
136
  }
108
137
 
109
138
  StorageAccessHelper.prototype.setCookieAndRedirect = function() {
110
- document.cookie = "shopify.cookies_persist=true";
139
+ this.setCookie('shopify.cookies_persist=true');
111
140
  var helper = this.setUpHelper();
112
141
  helper.redirect();
113
142
  }
@@ -9,7 +9,7 @@ module ShopifyApp
9
9
  include ShopifyApp::LoginProtection
10
10
  include ShopifyApp::EmbeddedApp
11
11
  before_action :login_again_if_different_user_or_shop
12
- around_action :shopify_session
12
+ around_action :activate_shopify_session
13
13
  end
14
14
  end
15
15
  end
@@ -8,6 +8,11 @@ module ShopifyApp
8
8
  def callback
9
9
  if auth_hash
10
10
  login_shop
11
+
12
+ if ShopifyApp::SessionRepository.user_storage.present? && user_session.blank?
13
+ return redirect_to(login_url_with_optional_shop)
14
+ end
15
+
11
16
  install_webhooks
12
17
  install_scripttags
13
18
  perform_after_authenticate_job
@@ -55,16 +60,15 @@ module ShopifyApp
55
60
  token: token,
56
61
  api_version: ShopifyApp.configuration.api_version
57
62
  )
58
- session[:shopify] = ShopifyApp::SessionRepository.store(session_store, user: associated_user)
59
- session[:shopify_domain] = shop_name
60
- session[:shopify_user] = associated_user
61
63
 
62
- if ShopifyApp.configuration.per_user_tokens?
63
- # Adds the user_session to the session to determine if the logged in user has changed
64
- user_session = auth_hash&.extra&.session
65
- raise IndexError, "Missing user session signature" if user_session.nil?
66
- session[:user_session] = user_session
64
+ session[:shopify_user] = associated_user
65
+ if session[:shopify_user].present?
66
+ session[:user_id] = ShopifyApp::SessionRepository.store_user_session(session_store, associated_user)
67
+ else
68
+ session[:shop_id] = ShopifyApp::SessionRepository.store_shop_session(session_store)
67
69
  end
70
+ session[:shopify_domain] = shop_name
71
+ session[:user_session] = auth_hash&.extra&.session
68
72
  end
69
73
 
70
74
  def install_webhooks
@@ -72,7 +76,7 @@ module ShopifyApp
72
76
 
73
77
  WebhooksManager.queue(
74
78
  shop_name,
75
- token,
79
+ shop_session&.token || user_session.token,
76
80
  ShopifyApp.configuration.webhooks
77
81
  )
78
82
  end
@@ -82,7 +86,7 @@ module ShopifyApp
82
86
 
83
87
  ScripttagsManager.queue(
84
88
  shop_name,
85
- token,
89
+ shop_session&.token || user_session.token,
86
90
  ShopifyApp.configuration.scripttags
87
91
  )
88
92
  end
@@ -3,6 +3,7 @@ module ShopifyApp
3
3
  include ShopifyApp::LoginProtection
4
4
 
5
5
  layout false, only: :new
6
+
6
7
  after_action only: [:new, :create] do |controller|
7
8
  controller.response.headers.except!('X-Frame-Options')
8
9
  end
@@ -16,30 +17,35 @@ module ShopifyApp
16
17
  end
17
18
 
18
19
  def enable_cookies
19
- return unless validate_shop
20
+ return unless validate_shop_presence
20
21
 
21
22
  render(:enable_cookies, layout: false, locals: {
22
23
  does_not_have_storage_access_url: top_level_interaction_path(
23
- shop: sanitized_shop_name
24
+ shop: sanitized_shop_name,
25
+ return_to: params[:return_to]
24
26
  ),
25
27
  has_storage_access_url: login_url_with_optional_shop(top_level: true),
26
- app_home_url: granted_storage_access_path(shop: sanitized_shop_name),
27
- current_shopify_domain: current_shopify_domain,
28
+ app_target_url: granted_storage_access_path(
29
+ shop: sanitized_shop_name,
30
+ return_to: params[:return_to]
31
+ ),
32
+ current_shopify_domain: current_shopify_domain
28
33
  })
29
34
  end
30
35
 
31
36
  def top_level_interaction
32
37
  @url = login_url_with_optional_shop(top_level: true)
33
- validate_shop
38
+ validate_shop_presence
34
39
  end
35
40
 
36
41
  def granted_storage_access
37
- return unless validate_shop
42
+ return unless validate_shop_presence
38
43
 
39
44
  session['shopify.granted_storage_access'] = true
40
45
 
41
- params = { shop: @shop }
42
- redirect_to("#{return_address}?#{params.to_query}")
46
+ copy_return_to_param_to_session
47
+
48
+ redirect_to(return_address_with_params({ shop: @shop }))
43
49
  end
44
50
 
45
51
  def destroy
@@ -54,7 +60,9 @@ module ShopifyApp
54
60
  return render_invalid_shop_error unless sanitized_shop_name.present?
55
61
  session['shopify.omniauth_params'] = { shop: sanitized_shop_name }
56
62
 
57
- session[:return_to] = params[:return_to] if params[:return_to]
63
+ copy_return_to_param_to_session
64
+
65
+ set_user_tokens_option
58
66
 
59
67
  if user_agent_can_partition_cookies
60
68
  authenticate_with_partitioning
@@ -83,7 +91,27 @@ module ShopifyApp
83
91
  end
84
92
  end
85
93
 
86
- def validate_shop
94
+ def set_user_tokens_option
95
+ if shop_session.blank?
96
+ session[:user_tokens] = false
97
+ return
98
+ end
99
+
100
+ session[:user_tokens] = ShopifyApp::SessionRepository.user_storage.present?
101
+
102
+ ShopifyAPI::Session.temp(
103
+ domain: shop_session.domain,
104
+ token: shop_session.token,
105
+ api_version: shop_session.api_version
106
+ ) do
107
+ ShopifyAPI::Metafield.find(:token_validity_bogus_check)
108
+ end
109
+ rescue ActiveResource::UnauthorizedAccess
110
+ session[:user_tokens] = false
111
+ rescue StandardError
112
+ end
113
+
114
+ def validate_shop_presence
87
115
  @shop = sanitized_shop_name
88
116
  unless @shop
89
117
  render_invalid_shop_error
@@ -93,6 +121,10 @@ module ShopifyApp
93
121
  true
94
122
  end
95
123
 
124
+ def copy_return_to_param_to_session
125
+ session[:return_to] = params[:return_to] if params[:return_to]
126
+ end
127
+
96
128
  def render_invalid_shop_error
97
129
  flash[:error] = I18n.t('invalid_shop_url')
98
130
  redirect_to return_address
@@ -133,11 +165,15 @@ module ShopifyApp
133
165
  layout: false,
134
166
  locals: {
135
167
  does_not_have_storage_access_url: top_level_interaction_path(
136
- shop: sanitized_shop_name
168
+ shop: sanitized_shop_name,
169
+ return_to: session[:return_to]
137
170
  ),
138
171
  has_storage_access_url: login_url_with_optional_shop(top_level: true),
139
- app_home_url: granted_storage_access_path(shop: sanitized_shop_name),
140
- current_shopify_domain: current_shopify_domain,
172
+ app_target_url: granted_storage_access_path(
173
+ shop: sanitized_shop_name,
174
+ return_to: session[:return_to]
175
+ ),
176
+ current_shopify_domain: current_shopify_domain
141
177
  }
142
178
  )
143
179
  end