shopify_app 12.0.7 → 13.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -6
  3. data/.travis.yml +4 -3
  4. data/CHANGELOG.md +33 -0
  5. data/Gemfile +3 -0
  6. data/README.md +98 -42
  7. data/Rakefile +1 -0
  8. data/app/controllers/concerns/shopify_app/authenticated.rb +1 -1
  9. data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
  10. data/app/controllers/shopify_app/callback_controller.rb +50 -17
  11. data/app/controllers/shopify_app/sessions_controller.rb +36 -10
  12. data/app/controllers/shopify_app/webhooks_controller.rb +6 -5
  13. data/config/locales/fi.yml +1 -1
  14. data/config/locales/nl.yml +7 -7
  15. data/config/routes.rb +1 -0
  16. data/docs/Releasing.md +1 -0
  17. data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +5 -3
  18. data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -0
  19. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +2 -1
  20. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +4 -4
  21. data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +5 -4
  22. data/lib/generators/shopify_app/add_webhook/templates/{webhook_job.rb → webhook_job.rb.tt} +5 -0
  23. data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +4 -3
  24. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +3 -3
  25. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +10 -9
  26. data/lib/generators/shopify_app/controllers/controllers_generator.rb +1 -0
  27. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +4 -3
  28. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +1 -1
  29. data/lib/generators/shopify_app/install/install_generator.rb +10 -9
  30. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
  31. data/lib/generators/shopify_app/install/templates/omniauth.rb +2 -1
  32. data/lib/generators/shopify_app/install/templates/{shopify_app.rb → shopify_app.rb.tt} +1 -1
  33. data/lib/generators/shopify_app/install/templates/shopify_provider.rb +1 -1
  34. data/lib/generators/shopify_app/install/templates/user_agent.rb +2 -1
  35. data/lib/generators/shopify_app/routes/routes_generator.rb +1 -0
  36. data/lib/generators/shopify_app/routes/templates/routes.rb +10 -9
  37. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +12 -7
  38. data/lib/generators/shopify_app/shop_model/templates/shop.rb +2 -1
  39. data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
  40. data/lib/generators/shopify_app/user_model/templates/user.rb +2 -1
  41. data/lib/generators/shopify_app/user_model/user_model_generator.rb +12 -7
  42. data/lib/generators/shopify_app/views/views_generator.rb +1 -0
  43. data/lib/shopify_app.rb +11 -4
  44. data/lib/shopify_app/configuration.rb +21 -11
  45. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +3 -2
  46. data/lib/shopify_app/controller_concerns/embedded_app.rb +3 -2
  47. data/lib/shopify_app/controller_concerns/localization.rb +1 -0
  48. data/lib/shopify_app/controller_concerns/login_protection.rb +71 -29
  49. data/lib/shopify_app/controller_concerns/webhook_verification.rb +2 -1
  50. data/lib/shopify_app/engine.rb +5 -0
  51. data/lib/shopify_app/jobs/scripttags_manager_job.rb +1 -1
  52. data/lib/shopify_app/jobs/webhooks_manager_job.rb +1 -1
  53. data/lib/shopify_app/managers/scripttags_manager.rb +4 -3
  54. data/lib/shopify_app/managers/webhooks_manager.rb +4 -3
  55. data/lib/shopify_app/middleware/jwt_middleware.rb +41 -0
  56. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +2 -1
  57. data/lib/shopify_app/session/in_memory_session_store.rb +7 -3
  58. data/lib/shopify_app/session/in_memory_shop_session_store.rb +14 -0
  59. data/lib/shopify_app/session/in_memory_user_session_store.rb +14 -0
  60. data/lib/shopify_app/session/jwt.rb +61 -0
  61. data/lib/shopify_app/session/null_user_session_store.rb +22 -0
  62. data/lib/shopify_app/session/session_repository.rb +36 -14
  63. data/lib/shopify_app/session/session_storage.rb +1 -10
  64. data/lib/shopify_app/session/shop_session_storage.rb +42 -0
  65. data/lib/shopify_app/session/user_session_storage.rb +42 -0
  66. data/lib/shopify_app/test_helpers/all.rb +2 -0
  67. data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +17 -0
  68. data/lib/shopify_app/utils.rb +6 -5
  69. data/lib/shopify_app/version.rb +2 -1
  70. data/package-lock.json +1231 -1210
  71. data/package.json +1 -1
  72. data/shopify_app.gemspec +13 -8
  73. data/yarn.lock +3 -3
  74. metadata +50 -14
  75. data/lib/shopify_app/session/storage_strategies/shop_storage_strategy.rb +0 -23
  76. data/lib/shopify_app/session/storage_strategies/user_storage_strategy.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5acb9f9fb1606515013edfda25f981c36cf5ab4323572764ba9715018056532
4
- data.tar.gz: e5499e7186fa12f37e01f975d7b67ed6a846d67679b580846a7191b53d81b3d3
3
+ metadata.gz: 342e069a4f4d0d9bd824403f44cbcf25e398c8e869b4c31058199c7a13daca19
4
+ data.tar.gz: cebba3a407077d4dd86183b6aa4fe702593cfacb5de7f630305fefb7bf0ed545
5
5
  SHA512:
6
- metadata.gz: c9e736b65bf5091c6ebdca30073653dde86abc131cd77747f872c0d42972bc35abdd406301e5a5eb0dd4f9273c66264c75b28e119d05d203e440038bb19250d3
7
- data.tar.gz: 264ed359e7e07cf7b0947cbafad738cabf220e470872c663f18c2507c17c7d994316af275f1d28820d5624df03fabadf6f213475fb793b4fd024de514b636cf0
6
+ metadata.gz: 3d7e6c9dc9a521fe022f91e3c2761ff451a34fb67a61ebe3ac5888e5a19924d8eea912a9d644c4255ab0570c959ee8f56f6b30b98809b9d6bb9e09323f7bfec3
7
+ data.tar.gz: 67ed9dbc2897dce008f35c760251d64c708319a2171617692caedf01026b8366dd5f3ac9609d23c2ee8d196d2b90a34adce2bcf2974e7541bfa24a74a06c303b
@@ -1,10 +1,16 @@
1
- inherit_from:
2
- - https://shopify.github.io/ruby-style-guide/rubocop.yml
1
+ inherit_gem:
2
+ rubocop-shopify: rubocop.yml
3
3
 
4
- LineLength:
4
+ AllCops:
5
+ TargetRubyVersion: 2.7
5
6
  Exclude:
6
- - test/**/*
7
+ - 'test/tmp/**/*'
7
8
 
8
- Metrics/ClassLength:
9
+ Style/MethodCallWithArgsParentheses:
9
10
  Exclude:
10
- - test/**/*
11
+ - '**/Gemfile'
12
+
13
+ Style/ClassAndModuleChildren:
14
+ Exclude:
15
+ - 'test/**/*'
16
+
@@ -13,9 +13,10 @@ cache:
13
13
  yarn: true
14
14
 
15
15
  rvm:
16
- - 2.4.3
17
- - 2.5.0
18
- - 2.6.2
16
+ - 2.4
17
+ - 2.5
18
+ - 2.6
19
+ - 2.7
19
20
 
20
21
  install:
21
22
  - bundle install
@@ -1,3 +1,36 @@
1
+ 13.2.0
2
+ ------
3
+ * Get current shop domain from JWT header
4
+ * Validate that the omniauth data matches the JWT data
5
+ * Persist the token information to the session store
6
+
7
+ 13.1.1
8
+ ------
9
+ * Update browser_sniffer to 1.2.2
10
+
11
+ 13.1.0
12
+ ------
13
+ * Adds the shop URL as a parameter when redirecting after the callback
14
+ * Bump minimum Ruby version to 2.4
15
+ * Bug fixes
16
+
17
+ 13.0.1
18
+ ------
19
+ * Small addition to WebhookJob to return if the shop is nil #952
20
+ * Added Rubocop to the Repo #948
21
+ * Added a WebhookVerification test helper #950
22
+ * Fix for deprecation warning while loading session storage at startup
23
+ * Changes that will allow future JWT authentication
24
+
25
+ 13.0.1
26
+ ------
27
+ * fix for deprecation warning while loading session storage at startup
28
+
29
+ 13.0.0
30
+ ------
31
+ + #887 Added concurrent user and shop session support (online/offline)
32
+ BREAKING, please see README for migration notes.
33
+
1
34
  12.0.7
2
35
  ------
3
36
  * Remove check for API_KEY in config that was throwing errors during install #919
data/Gemfile CHANGED
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  source "https://rubygems.org"
2
3
 
3
4
  # Specify your gem's dependencies in shopify_app.gemspec
4
5
  gemspec
5
6
 
6
7
  gem 'rails-controller-testing', group: :test
8
+
9
+ gem 'rubocop-shopify', require: false
data/README.md CHANGED
@@ -8,7 +8,7 @@ Shopify App
8
8
 
9
9
  Shopify Application Rails engine and generator
10
10
 
11
- #### NOTE : Versions 8.0.0 through 8.2.3 contained a CSRF vulnerability that was addressed in version 8.2.4. Please update to version 8.2.4 if you're using an old version.
11
+ #### NOTE: Versions 8.0.0 through 8.2.3 contained a CSRF vulnerability that was addressed in version 8.2.4. Please update to version 8.2.4 if you're using an old version.
12
12
 
13
13
  Table of Contents
14
14
  -----------------
@@ -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-1300)
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)
@@ -33,7 +34,7 @@ Introduction
33
34
  -----------
34
35
  Get started with the [Shopify Admin API](https://help.shopify.com/en/api/getting-started) faster; This gem includes a Rails Engine and generators for writing Rails applications using the Shopify API. The Engine provides a SessionsController and all the required code for authenticating with a shop via Oauth (other authentication methods are not supported).
35
36
 
36
- *Note: It's recommended to use this on a new Rails project, so that the generator won't overwrite/delete your files.*
37
+ *Note: It's recommended to use this on a new Rails project so that the generator won't overwrite/delete your files.*
37
38
 
38
39
  Learn how to create and deploy a new Shopify App to Heroku with our [quickstart guide](https://github.com/Shopify/shopify_app/blob/master/docs/Quickstart.md), or dive in in less than 5 minutes with this quickstart video:
39
40
 
@@ -41,15 +42,15 @@ Learn how to create and deploy a new Shopify App to Heroku with our [quickstart
41
42
 
42
43
  Become a Shopify App Developer
43
44
  --------------------------------
44
- 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
+ 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
  ------------
52
- To get started add `shopify_app` to your Gemfile and run `bundle install`:
53
+ To get started, add `shopify_app` to your Gemfile and run `bundle install`:
53
54
 
54
55
  ``` sh
55
56
  # Create a new rails app
@@ -66,7 +67,7 @@ Now we are ready to run any of the [generators](#generators) included with `shop
66
67
 
67
68
  #### Rails Compatibility
68
69
 
69
- The lastest version of shopify_app is compatible with Rails `>= 5`. Use version `<= v7.2.8` if you need to work with Rails 4.
70
+ The latest version of shopify_app is compatible with Rails `>= 5`. Use version `<= v7.2.8` if you need to work with Rails 4.
70
71
 
71
72
 
72
73
  Generators
@@ -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
 
@@ -93,7 +94,7 @@ SHOPIFY_API_SECRET=your api secret
93
94
 
94
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.
95
96
 
96
- **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.**
97
+ **You will need to load the ENV variables into your environment, you can do this with the [dot-env](https://github.com/bkeepers/dotenv) gem or any other method you wish to.**
97
98
 
98
99
  ### Install Generator
99
100
 
@@ -107,12 +108,12 @@ $ rails generate shopify_app:install
107
108
 
108
109
  Other options include:
109
110
  * `application_name` - the name of your app, it can be supplied with or without double-quotes if a whitespace is present. (e.g. `--application_name Example App` or `--application_name "Example App"`)
110
- * `scope` - the Oauth access scope required for your app, eg **read_products, write_orders**. *Multiple options* need to be delimited by a comma-space, and can be supplied with or without double-quotes
111
+ * `scope` - the Oauth access scope required for your app, e.g. **read_products, write_orders**. *Multiple options* need to be delimited by a comma-space and can be supplied with or without double-quotes
111
112
  (e.g. `--scope read_products, write_orders, write_products` or `--scope "read_products, write_orders, write_products"`)
112
113
  For more information, refer the [docs](http://docs.shopify.com/api/tutorials/oauth).
113
114
  * `embedded` - the default is to generate an [embedded app](http://docs.shopify.com/embedded-app-sdk), if you want a legacy non-embedded app then set this to false, `--embedded false`
114
115
 
115
- You can update any of these settings later on easily, the arguments are simply for convenience.
116
+ You can update any of these settings later on easily; the arguments are simply for convenience.
116
117
 
117
118
  The generator adds ShopifyApp and the required initializers to the host Rails application.
118
119
 
@@ -125,7 +126,7 @@ After running the `install` generator, you can start your app with `bundle exec
125
126
  $ rails generate shopify_app:home_controller
126
127
  ```
127
128
 
128
- This generator creates an example home controller and view which fetches and displays products using the Shopify API
129
+ This generator creates an example home controller and view which fetches and displays products using the Shopify API.
129
130
 
130
131
 
131
132
  ### App Proxy Controller Generator
@@ -134,7 +135,7 @@ This generator creates an example home controller and view which fetches and dis
134
135
  $ rails generate shopify_app:app_proxy_controller
135
136
  ```
136
137
 
137
- This optional generator, not included with the default generator, creates the app proxy controller to handle proxy requests to the app from your shop storefront, modifies 'config/routes.rb' with a namespace route, and an example view which displays current shop information using the LiquidAPI
138
+ This optional generator, not included with the default generator, creates the app proxy controller to handle proxy requests to the app from your shop storefront, modifies 'config/routes.rb' with a namespace route, and an example view which displays current shop information using the LiquidAPI.
138
139
 
139
140
  ### Marketing Extension Generator
140
141
 
@@ -146,7 +147,7 @@ This will create a controller with the endpoints required to build a [marketing
146
147
 
147
148
  ### Controllers, Routes and Views
148
149
 
149
- The last group of generators are for your convenience if you want to start overriding code included as part of the Rails engine. For example by default the engine provides a simple SessionController, if you run the `rails generate shopify_app:controllers` generator then this code gets copied out into your app so you can start adding to it. Routes and views follow the exact same pattern.
150
+ The last group of generators are for your convenience if you want to start overriding code included as part of the Rails engine. For example, by default the engine provides a simple SessionController, if you run the `rails generate shopify_app:controllers` generator then this code gets copied out into your app so you can start adding to it. Routes and views follow the exact same pattern.
150
151
 
151
152
  Mounting the Engine
152
153
  -------------------
@@ -169,7 +170,7 @@ The engine may also be mounted at a nested route, for example:
169
170
  mount ShopifyApp::Engine, at: '/nested'
170
171
  ```
171
172
 
172
- This will create the Shopify engine routes under the specified subpath. You'll also need to make some updates to your `shopify_app.rb` and `omniauth.rb` initializers. First update the shopify_app initializer to include a custom `root_url` e.g.:
173
+ This will create the Shopify engine routes under the specified subpath. You'll also need to make some updates to your `shopify_app.rb` and `omniauth.rb` initializers. First, update the shopify_app initializer to include a custom `root_url` e.g.:
173
174
 
174
175
  ```ruby
175
176
  ShopifyApp.configure do |config|
@@ -215,7 +216,7 @@ Authentication
215
216
 
216
217
  ### Callback
217
218
 
218
- 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:
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:
219
220
  * Logging into the shop and resetting the session
220
221
  * [Installing Webhooks](https://github.com/Shopify/shopify_app#webhooksmanager)
221
222
  * [Setting Scripttags](https://github.com/Shopify/shopify_app#scripttagsmanager)
@@ -226,44 +227,45 @@ Upon completing the authentication flow Shopify calls the app at the `callback_p
226
227
 
227
228
  ### ShopifyApp::SessionRepository
228
229
 
229
- `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.
230
-
231
- 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.
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.
232
231
 
233
232
  #### Shop-based token storage
234
- 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.
233
+ Storing tokens on the store model means that any user login associated with the store will have equal access levels to whatever the original user granted the app.
235
234
  ```sh
236
235
  $ rails generate shopify_app:shop_model
237
236
  ```
238
237
  This will generate a shop model which will be the storage for the tokens necessary for authentication.
239
238
 
240
239
  #### User-based token storage
241
- 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 the 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.
242
241
  ```sh
242
+ $ rails generate shopify_app:shop_model
243
243
  $ rails generate shopify_app:user_model
244
244
  ```
245
- 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.
246
246
 
247
247
  The current Shopify user will be stored in the rails session at `session[:shopify_user]`
248
248
 
249
- 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.
250
-
251
- 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).
252
250
 
253
251
  #### Migrating from shop-based to user-based token strategy
254
- After running the generator, ensure that configuration settings are successfully changed:
255
-
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:
256
255
  ```ruby
257
256
  # In the `omniauth.rb` initializer:
258
257
  provider :shopify,
259
- ShopifyApp.configuration.api_key,
260
- ShopifyApp.configuration.secret,
261
- scope: ShopifyApp.configuration.scope,
262
- 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
+ }
263
265
 
264
266
  # In the `shopify_app.rb` initializer:
265
- config.session_repository = 'User'
266
- config.per_user_tokens = true
267
+ config.shop_session_repository = {YOUR_SHOP_MODEL_CLASS}
268
+ config.user_session_repository = {YOUR_USER_MODEL_CLASS}
267
269
  ```
268
270
 
269
271
  ### Authenticated
@@ -274,7 +276,7 @@ For backwards compatibility, the engine still provides a controller called `Shop
274
276
 
275
277
  ### AfterAuthenticate Job
276
278
 
277
- 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:
279
+ 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:
278
280
 
279
281
  ```ruby
280
282
  ShopifyApp.configure do |config|
@@ -322,11 +324,11 @@ ShopifyApp.configure do |config|
322
324
  end
323
325
  ```
324
326
 
325
- When the 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.
327
+ When the 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.
326
328
 
327
- ShopifyApp also provides a WebhooksController 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).
329
+ ShopifyApp also provides a WebhooksController 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).
328
330
 
329
- If you would like to namespace your jobs you may set `webhook_jobs_namespace` in the config. For example if your app handles webhooks from other ecommerce applications as well, and you want Shopify cart update webhooks to be processed by a job living in `jobs/shopify/webhooks/carts_update_job.rb` rather than `jobs/carts_update_job.rb`):
331
+ If you would like to namespace your jobs, you may set `webhook_jobs_namespace` in the config. For example, if your app handles webhooks from other ecommerce applications as well, and you want Shopify cart update webhooks to be processed by a job living in `jobs/shopify/webhooks/carts_update_job.rb` rather than `jobs/carts_update_job.rb`):
330
332
 
331
333
  ```ruby
332
334
  ShopifyApp.configure do |config|
@@ -364,9 +366,9 @@ class CustomWebhooksController < ApplicationController
364
366
  end
365
367
  ```
366
368
 
367
- The module skips the `verify_authenticity_token` before_action and adds an action to verify that the webhook came from Shopify. You can now add a post route to your application pointing to the controller and action to accept the webhook data from Shopify.
369
+ The module skips the `verify_authenticity_token` before_action and adds an action to verify that the webhook came from Shopify. You can now add a post route to your application, pointing to the controller and action to accept the webhook data from Shopify.
368
370
 
369
- The WebhooksManager uses ActiveJob, if ActiveJob is not configured then by default Rails will run the jobs inline. However it is highly recommended to configure a proper background processing queue like sidekiq or resque in production.
371
+ The WebhooksManager uses ActiveJob. If ActiveJob is not configured then by default Rails will run the jobs inline. However, it is highly recommended to configure a proper background processing queue like sidekiq or resque in production.
370
372
 
371
373
  ShopifyApp can create webhooks for you using the `add_webhook` generator. This will add the new webhook to your config and create the required job class for you.
372
374
 
@@ -374,7 +376,7 @@ ShopifyApp can create webhooks for you using the `add_webhook` generator. This w
374
376
  rails g shopify_app:add_webhook -t carts/update -a https://example.com/webhooks/carts_update
375
377
  ```
376
378
 
377
- where `-t` is the topic and `-a` is the address the webhook should be sent to.
379
+ Where `-t` is the topic and `-a` is the address the webhook should be sent to.
378
380
 
379
381
  ScripttagsManager
380
382
  -----------------
@@ -429,7 +431,7 @@ strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
429
431
  App Tunneling
430
432
  -------------
431
433
 
432
- 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.
433
435
 
434
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.
435
437
 
@@ -470,6 +472,31 @@ Troubleshooting
470
472
 
471
473
  see [TROUBLESHOOTING.md](https://github.com/Shopify/shopify_app/blob/master/docs/Troubleshooting.md)
472
474
 
475
+ Using Test Helpers inside your Application
476
+ -----------------------------------------
477
+
478
+ A test helper that will allow you to test `ShopifyApp::WebhookVerification` in the controller from your app, to use this test, you need to `require` it directly inside your app `test/controllers/webhook_verification_test.rb`.
479
+
480
+ ```ruby
481
+ require 'test_helper'
482
+ require 'action_controller'
483
+ require 'action_controller/base'
484
+ require 'shopify_app/test_helpers/webhook_verification_helper'
485
+ ```
486
+
487
+ Or you can require in your `test/test_helper.rb`.
488
+
489
+ ```ruby
490
+ ENV['RAILS_ENV'] ||= 'test'
491
+ require_relative '../config/environment'
492
+ require 'rails/test_help'
493
+ require 'byebug'
494
+ require 'shopify_app/test_helpers/all'
495
+ ```
496
+
497
+ With `lib/shopify_app/test_helpers/all'` more tests can be added and will only need to be required in once in your library.
498
+
499
+
473
500
  Testing an embedded app outside the Shopify admin
474
501
  -------------------------------------------------
475
502
 
@@ -479,6 +506,35 @@ By default, loading your embedded app will redirect to the Shopify admin, with t
479
506
  forceRedirect: <%= Rails.env.development? || Rails.env.test? ? 'false' : 'true' %>
480
507
  ```
481
508
 
509
+ Migrating to 13.0.0
510
+ -------------------
511
+
512
+ Version 13.0.0 adds the ability to use both user and shop sessions, concurrently. This however involved a large
513
+ change to how session stores work. Here are the steps to migrate to 13.x
514
+
515
+ ### Changes to `config/initializers/shopify_app.rb`
516
+ - *REMOVE* `config.per_user_tokens = [true|false]` this is no longer needed
517
+ - *CHANGE* `config.session_repository = 'Shop'` To `config.shop_session_repository = 'Shop'`
518
+ - *ADD (optional)* User Session Storage `config.user_session_repository = 'User'`
519
+
520
+ ### Shop Model Changes (normally `app/models/shop.rb`)
521
+ - *CHANGE* `include ShopifyApp::SessionStorage` to `include ShopifyApp::ShopSessionStorage`
522
+
523
+ ### Changes to the @shop_session instance variable (normally in `app/controllers/*.rb`)
524
+ - *CHANGE* if you are using shop sessions, `@shop_session` will need to be changed to `@current_shopify_session`.
525
+
526
+ ### Changes to Rails `session`
527
+ - *CHANGE* `session[:shopify]` is no longer set. Use `session[:user_id]` if your app uses user based tokens, or `session[:shop_id]` if your app uses shop based tokens.
528
+
529
+ ### Changes to `ShopifyApp::LoginProtection`
530
+ `ShopifyApp::LoginProtection`
531
+
532
+ if you are using `ShopifyApp::LoginProtection#shop_session` in your code, it will need to be
533
+ changed to `ShopifyApp::LoginProtection#activate_shopify_session`
534
+
535
+ ### Notes
536
+ You do not need a user model; a shop session is fine for most applications.
537
+
482
538
  Questions or problems?
483
539
  ----------------------
484
540
 
@@ -523,7 +579,7 @@ config.api_version = '2019-04'
523
579
 
524
580
  ### Session storage change
525
581
 
526
- You will need to add an `api_version` method to you session storage object. The default implementation for this is.
582
+ You will need to add an `api_version` method to your session storage object. The default implementation for this is.
527
583
  ```ruby
528
584
  def api_version
529
585
  ShopifyApp.configuration.api_version
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'bundler/gem_tasks'
2
3
  require 'rake/testtask'
3
4
 
@@ -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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  class AuthenticatedController < ActionController::Base
3
4
  include ShopifyApp::Authenticated
@@ -6,24 +6,54 @@ module ShopifyApp
6
6
  include ShopifyApp::LoginProtection
7
7
 
8
8
  def callback
9
- if auth_hash
10
- login_shop
9
+ unless auth_hash
10
+ return respond_with_error
11
+ end
12
+
13
+ if jwt_request? && !valid_jwt_auth?
14
+ return respond_with_error
15
+ end
16
+
17
+ if jwt_request?
18
+ set_shopify_session
19
+ head(:ok)
20
+ else
21
+ reset_session_options
22
+ set_shopify_session
23
+
24
+ if redirect_for_user_token?
25
+ return redirect_to(login_url_with_optional_shop)
26
+ end
27
+
11
28
  install_webhooks
12
29
  install_scripttags
13
30
  perform_after_authenticate_job
14
31
 
15
- redirect_to return_address
32
+ redirect_to(return_address)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def respond_with_error
39
+ if jwt_request?
40
+ head(:unauthorized)
16
41
  else
17
42
  flash[:error] = I18n.t('could_not_log_in')
18
43
  redirect_to(login_url_with_optional_shop)
19
44
  end
20
45
  end
21
46
 
22
- private
47
+ def redirect_for_user_token?
48
+ ShopifyApp::SessionRepository.user_storage.present? && user_session.blank?
49
+ end
50
+
51
+ def jwt_request?
52
+ jwt_shopify_domain || jwt_shopify_user_id
53
+ end
23
54
 
24
- def login_shop
25
- reset_session_options
26
- set_shopify_session
55
+ def valid_jwt_auth?
56
+ auth_hash && jwt_shopify_domain == shop_name && jwt_shopify_user_id == associated_user_id
27
57
  end
28
58
 
29
59
  def auth_hash
@@ -40,6 +70,10 @@ module ShopifyApp
40
70
  auth_hash['extra']['associated_user']
41
71
  end
42
72
 
73
+ def associated_user_id
74
+ associated_user && associated_user['id']
75
+ end
76
+
43
77
  def token
44
78
  auth_hash['credentials']['token']
45
79
  end
@@ -55,16 +89,15 @@ module ShopifyApp
55
89
  token: token,
56
90
  api_version: ShopifyApp.configuration.api_version
57
91
  )
58
- session[:shopify] = ShopifyApp::SessionRepository.store(session_store, user: associated_user)
59
- session[:shopify_domain] = shop_name
60
- session[:shopify_user] = associated_user
61
92
 
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
93
+ session[:shopify_user] = associated_user
94
+ if session[:shopify_user].present?
95
+ session[:user_id] = ShopifyApp::SessionRepository.store_user_session(session_store, associated_user)
96
+ else
97
+ session[:shop_id] = ShopifyApp::SessionRepository.store_shop_session(session_store)
67
98
  end
99
+ session[:shopify_domain] = shop_name
100
+ session[:user_session] = auth_hash&.extra&.session
68
101
  end
69
102
 
70
103
  def install_webhooks
@@ -72,7 +105,7 @@ module ShopifyApp
72
105
 
73
106
  WebhooksManager.queue(
74
107
  shop_name,
75
- token,
108
+ shop_session&.token || user_session.token,
76
109
  ShopifyApp.configuration.webhooks
77
110
  )
78
111
  end
@@ -82,7 +115,7 @@ module ShopifyApp
82
115
 
83
116
  ScripttagsManager.queue(
84
117
  shop_name,
85
- token,
118
+ shop_session&.token || user_session.token,
86
119
  ShopifyApp.configuration.scripttags
87
120
  )
88
121
  end