shopify_app 13.0.0 → 18.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/ISSUE_TEMPLATE/bug-report.md +63 -0
- data/.github/ISSUE_TEMPLATE/config.yml +1 -0
- data/.github/ISSUE_TEMPLATE/feature-request.md +33 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
- data/.github/workflows/build.yml +41 -0
- data/.github/workflows/release.yml +24 -0
- data/.github/workflows/rubocop.yml +22 -0
- data/.gitignore +0 -2
- data/.rubocop.yml +14 -6
- data/CHANGELOG.md +158 -0
- data/CONTRIBUTING.md +76 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +268 -0
- data/README.md +73 -534
- data/Rakefile +1 -0
- data/SECURITY.md +59 -0
- data/app/assets/images/storage_access.svg +1 -2
- data/app/assets/javascripts/shopify_app/post_redirect.js +9 -0
- data/app/assets/javascripts/shopify_app/storage_access.js +2 -1
- data/app/assets/javascripts/shopify_app/top_level_interaction.js +1 -1
- data/app/controllers/concerns/shopify_app/authenticated.rb +1 -0
- data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +26 -0
- data/app/controllers/concerns/shopify_app/require_known_shop.rb +39 -0
- data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +32 -0
- data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
- data/app/controllers/shopify_app/callback_controller.rb +104 -18
- data/app/controllers/shopify_app/extension_verification_controller.rb +2 -7
- data/app/controllers/shopify_app/sessions_controller.rb +26 -10
- data/app/controllers/shopify_app/webhooks_controller.rb +6 -5
- data/app/views/shopify_app/partials/_button_styles.html.erb +41 -36
- data/app/views/shopify_app/partials/_card_styles.html.erb +3 -3
- data/app/views/shopify_app/partials/_empty_state_styles.html.erb +28 -59
- data/app/views/shopify_app/partials/_form_styles.html.erb +56 -0
- data/app/views/shopify_app/partials/_layout_styles.html.erb +16 -1
- data/app/views/shopify_app/partials/_typography_styles.html.erb +6 -6
- data/app/views/shopify_app/sessions/enable_cookies.html.erb +2 -7
- data/app/views/shopify_app/sessions/new.html.erb +38 -110
- data/app/views/shopify_app/sessions/request_storage_access.html.erb +1 -1
- data/app/views/shopify_app/sessions/top_level_interaction.html.erb +21 -22
- data/app/views/shopify_app/shared/post_redirect_to_auth_shopify.html.erb +13 -0
- data/config/locales/de.yml +11 -11
- data/config/locales/fi.yml +1 -1
- data/config/locales/nl.yml +8 -8
- data/config/locales/th.yml +4 -4
- data/config/locales/vi.yml +22 -0
- data/config/locales/zh-CN.yml +1 -1
- data/config/routes.rb +1 -0
- data/docs/Quickstart.md +15 -87
- data/docs/Releasing.md +18 -14
- data/docs/Troubleshooting.md +147 -4
- data/docs/Upgrading.md +126 -0
- data/docs/shopify_app/authentication.md +124 -0
- data/docs/shopify_app/engine.md +82 -0
- data/docs/shopify_app/generators.md +127 -0
- data/docs/shopify_app/handling-access-scopes-changes.md +14 -0
- data/docs/shopify_app/script-tags.md +28 -0
- data/docs/shopify_app/session-repository.md +88 -0
- data/docs/shopify_app/testing.md +38 -0
- data/docs/shopify_app/webhooks.md +72 -0
- data/karma.conf.js +1 -1
- data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +5 -3
- data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -0
- data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +2 -1
- data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +4 -4
- data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +5 -4
- data/lib/generators/shopify_app/add_webhook/templates/{webhook_job.rb → webhook_job.rb.tt} +5 -0
- data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +4 -3
- data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +3 -3
- data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +10 -9
- data/lib/generators/shopify_app/authenticated_controller/authenticated_controller_generator.rb +1 -1
- data/lib/generators/shopify_app/controllers/controllers_generator.rb +2 -1
- data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +31 -3
- data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +10 -0
- data/lib/generators/shopify_app/home_controller/templates/index.html.erb +66 -16
- data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +12 -0
- data/lib/generators/shopify_app/install/install_generator.rb +46 -11
- data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +3 -2
- data/lib/generators/shopify_app/install/templates/flash_messages.js +0 -2
- data/lib/generators/shopify_app/install/templates/omniauth.rb +3 -1
- data/lib/generators/shopify_app/install/templates/shopify_app.js +1 -1
- data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +25 -0
- data/lib/generators/shopify_app/install/templates/shopify_provider.rb.tt +8 -0
- data/lib/generators/shopify_app/install/templates/user_agent.rb +2 -1
- data/lib/generators/shopify_app/products_controller/products_controller_generator.rb +19 -0
- data/lib/generators/shopify_app/products_controller/templates/products_controller.rb +8 -0
- data/lib/generators/shopify_app/routes/routes_generator.rb +1 -0
- data/lib/generators/shopify_app/routes/templates/routes.rb +10 -9
- data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +39 -7
- data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_scopes_column.erb +5 -0
- data/lib/generators/shopify_app/shop_model/templates/shop.rb +2 -1
- data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
- data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_access_scopes_column.erb +5 -0
- data/lib/generators/shopify_app/user_model/templates/user.rb +2 -1
- data/lib/generators/shopify_app/user_model/user_model_generator.rb +39 -7
- data/lib/generators/shopify_app/views/views_generator.rb +2 -1
- data/lib/shopify_app/access_scopes/noop_strategy.rb +13 -0
- data/lib/shopify_app/access_scopes/shop_strategy.rb +24 -0
- data/lib/shopify_app/access_scopes/user_strategy.rb +41 -0
- data/lib/shopify_app/configuration.rb +40 -8
- data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +3 -3
- data/lib/shopify_app/controller_concerns/csrf_protection.rb +15 -0
- data/lib/shopify_app/controller_concerns/embedded_app.rb +3 -2
- data/lib/shopify_app/controller_concerns/localization.rb +1 -0
- data/lib/shopify_app/controller_concerns/login_protection.rb +81 -16
- data/lib/shopify_app/controller_concerns/payload_verification.rb +24 -0
- data/lib/shopify_app/controller_concerns/webhook_verification.rb +3 -18
- data/lib/shopify_app/engine.rb +27 -0
- data/lib/shopify_app/jobs/scripttags_manager_job.rb +1 -1
- data/lib/shopify_app/jobs/webhooks_manager_job.rb +1 -1
- data/lib/shopify_app/managers/scripttags_manager.rb +4 -3
- data/lib/shopify_app/managers/webhooks_manager.rb +4 -3
- data/lib/shopify_app/middleware/jwt_middleware.rb +42 -0
- data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +3 -2
- data/lib/shopify_app/omniauth/omniauth_configuration.rb +64 -0
- data/lib/shopify_app/session/in_memory_session_store.rb +7 -3
- data/lib/shopify_app/session/in_memory_shop_session_store.rb +12 -0
- data/lib/shopify_app/session/in_memory_user_session_store.rb +12 -0
- data/lib/shopify_app/session/jwt.rb +63 -0
- data/lib/shopify_app/session/null_user_session_store.rb +22 -0
- data/lib/shopify_app/session/session_repository.rb +13 -16
- data/lib/shopify_app/session/session_storage.rb +1 -0
- data/lib/shopify_app/session/shop_session_storage.rb +21 -9
- data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +58 -0
- data/lib/shopify_app/session/user_session_storage.rb +19 -8
- data/lib/shopify_app/session/user_session_storage_with_scopes.rb +58 -0
- data/lib/shopify_app/test_helpers/all.rb +2 -0
- data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +17 -0
- data/lib/shopify_app/utils.rb +18 -5
- data/lib/shopify_app/version.rb +2 -1
- data/lib/shopify_app.rb +23 -5
- data/package.json +7 -8
- data/service.yml +1 -4
- data/shopify_app.gemspec +15 -9
- data/translation.yml +1 -1
- data/yarn.lock +2140 -2188
- metadata +107 -19
- data/.github/ISSUE_TEMPLATE.md +0 -14
- data/.travis.yml +0 -27
- data/docs/install-on-dev-shop.png +0 -0
- data/docs/test-your-app.png +0 -0
- data/lib/generators/shopify_app/install/templates/shopify_app.rb +0 -15
- data/lib/generators/shopify_app/install/templates/shopify_provider.rb +0 -20
- data/package-lock.json +0 -7245
data/docs/Releasing.md
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
-
Releasing ShopifyApp
|
1
|
+
# Releasing ShopifyApp
|
2
2
|
|
3
|
-
1.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
1. Make the code changes in a separate PR that doesn't modify the version.
|
4
|
+
1. After that is merged, check the Semantic Versioning page for info on how to version the new release: http://semver.org
|
5
|
+
1. Create a pull request with the following changes:
|
6
|
+
- Update the version of ShopifyApp in lib/shopify_app/version.rb
|
7
|
+
- Update the version of shopify_app in package.json
|
8
|
+
- Run `bundle` to update `Gemfile.lock`
|
9
|
+
- Add a CHANGELOG entry for the new release with the date
|
10
|
+
- Change the title of the PR to something like: "Packaging for release X.Y.Z"
|
11
|
+
1. Merge your pull request
|
12
|
+
1. Checkout and pull from master so you have the latest version of the shopify_app
|
13
|
+
1. Tag the HEAD with the version
|
14
|
+
```bash
|
15
|
+
$ git tag -f vX.Y.Z && git push --tags --force
|
16
|
+
```
|
17
|
+
1. Check that Create Release workflow successfully runs
|
18
|
+
1. Use Shipit to build and push the gem
|
15
19
|
|
16
|
-
If you see an error like 'You need to create the vX.Y.X tag first', clear
|
20
|
+
If you see an error like 'You need to create the vX.Y.X tag first', clear git
|
17
21
|
cache in Shipit settings
|
data/docs/Troubleshooting.md
CHANGED
@@ -1,7 +1,27 @@
|
|
1
|
-
Troubleshooting Shopify App
|
2
|
-
===========
|
1
|
+
# Troubleshooting Shopify App
|
3
2
|
|
4
|
-
|
3
|
+
#### Table of contents
|
4
|
+
|
5
|
+
[Generators](#generators)
|
6
|
+
* [The `shopify_app:install` generator hangs](#the-shopifyappinstall-generator-hangs)
|
7
|
+
|
8
|
+
[Rails](#rails)
|
9
|
+
* [Known issues with Rails `v6.1`](#known-issues-with-rails-v61)
|
10
|
+
|
11
|
+
[App installation](#app-installation)
|
12
|
+
* [My app won't install](#my-app-wont-install)
|
13
|
+
* [My app keeps redirecting to login](#my-app-keeps-redirecting-to-login)
|
14
|
+
* [My app returns 401 during oauth](#my-app-returns-401-during-oauth)
|
15
|
+
|
16
|
+
[JWT session tokens](#jwt-session-tokens)
|
17
|
+
* [My app is still using cookies to authenticate](#my-app-is-still-using-cookies-to-authenticate)
|
18
|
+
* [My app can't make requests to the Shopify API](#my-app-cant-make-requests-to-the-shopify-api)
|
19
|
+
|
20
|
+
[Migrating to App Bridge 2.0](#migrating-to-app-bridge-2.0)
|
21
|
+
|
22
|
+
## Generators
|
23
|
+
|
24
|
+
### The shopify_app:install generator hangs
|
5
25
|
|
6
26
|
Rails uses spring by default to speed up development. To run the generator, spring has to be stopped:
|
7
27
|
|
@@ -11,6 +31,129 @@ $ bundle exec spring stop
|
|
11
31
|
|
12
32
|
Run shopify_app generator again.
|
13
33
|
|
14
|
-
|
34
|
+
## Rails
|
35
|
+
|
36
|
+
### Known issues with Rails `v6.1`
|
37
|
+
|
38
|
+
If you recently upgraded your application's `Rails::Application` configuration to load the default configuration for Rails `v6.1`, then you will need to update the following `cookies_same_site_protection` ActionDispatch configuration.
|
39
|
+
|
40
|
+
```diff
|
41
|
+
# config/application.rb
|
42
|
+
|
43
|
+
require_relative 'boot'
|
44
|
+
|
45
|
+
require 'rails/all'
|
46
|
+
|
47
|
+
Bundler.require(*Rails.groups)
|
48
|
+
|
49
|
+
module AppName
|
50
|
+
class Application < Rails::Application
|
51
|
+
+ config.load_defaults 6.1
|
52
|
+
|
53
|
+
+ config.action_dispatch.cookies_same_site_protection = :none
|
54
|
+
...
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
As of Rails `v6.1`, the same-site cookie protection setting defaults to `Lax`. This does not allow an embedded app to make cross-domain requests in the Shopify Admin.
|
60
|
+
|
61
|
+
Alternatively, you can upgrade to [`v17.2.0` of the shopify_app gem](/docs/Upgrading.md#upgrading-to-v1720).
|
62
|
+
|
63
|
+
## App installation
|
64
|
+
|
65
|
+
### My app won't install
|
66
|
+
|
67
|
+
#### App installation fails with 'The page you’re looking for could not be found' if the app was installed before
|
15
68
|
|
16
69
|
This issue can occur when the session (the model you set as `ShopifyApp::SessionRepository.storage`) isn't deleted when the user uninstalls your app. A possible fix for this is listening to the `app/uninstalled` webhook and deleting the corresponding session in the webhook handler.
|
70
|
+
|
71
|
+
### My app returns 401 during oauth
|
72
|
+
|
73
|
+
If your local dev env uses the `cookie_store` session storage strategy, you may encounter 401 errors during oauth due to a race condition between asset requests and `/auth/shopify`. You should be able to work around for local testing by using a different browser or session storage strategy. [Read more about the status of this issue](https://github.com/Shopify/shopify_app/issues/1269).
|
74
|
+
|
75
|
+
## JWT session tokens
|
76
|
+
|
77
|
+
### My app is still using cookies to authenticate
|
78
|
+
|
79
|
+
#### `shopify_app` gem version
|
80
|
+
|
81
|
+
Ensure the app is using shopify_app gem v13.x.x+. See [*Upgrading to `v13.0.0`*](/docs/Upgrading.md#upgrading-to-v1300).
|
82
|
+
|
83
|
+
#### `shopify_app` gem Rails configuration
|
84
|
+
|
85
|
+
Edit `config/initializer/shopify_app.rb` and ensure the following configurations are set:
|
86
|
+
|
87
|
+
```diff
|
88
|
+
+ config.embedded_app = true
|
89
|
+
|
90
|
+
+ config.allow_jwt_authentication = true
|
91
|
+
+ config.allow_cookie_authentication = false
|
92
|
+
|
93
|
+
# This line should already exist if you're using shopify_app gem 13.x.x+
|
94
|
+
+ config.shop_session_repository = 'Shop'
|
95
|
+
```
|
96
|
+
|
97
|
+
#### Inspect server logs
|
98
|
+
|
99
|
+
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.
|
100
|
+
|
101
|
+
In this case, check the server logs to see if the session token was invalid:
|
102
|
+
|
103
|
+
```los
|
104
|
+
[ShopifyApp::JWT] Failed to validate JWT: [JWT::<Error>] <Failure message>
|
105
|
+
```
|
106
|
+
|
107
|
+
*Example*
|
108
|
+
|
109
|
+
```
|
110
|
+
[ShopifyApp::JWT] Failed to validate JWT: [JWT::ImmatureSignature] Signature nbf has not been reached
|
111
|
+
```
|
112
|
+
|
113
|
+
**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.
|
114
|
+
|
115
|
+
```diff
|
116
|
+
- gem 'shopify_app', '~> 14.2'
|
117
|
+
+ gem 'shopify_app', path: '/path/to/shopify_app'
|
118
|
+
```
|
119
|
+
|
120
|
+
### My app can't make requests to the Shopify API
|
121
|
+
|
122
|
+
> **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).
|
123
|
+
|
124
|
+
#### The Shopify API returns `401 Unauthorized`
|
125
|
+
|
126
|
+
If your app uses [user-based token storage](/docs/shopify_app/session-repository.md#user-based-token-storage), then your app is configured to use **online** access tokens (see [API access modes](https://shopify.dev/concepts/about-apis/authentication#api-access-modes) to learn the difference between "online" and "offline" access tokens ). Unlike offline access tokens, online access tokens expire daily and cannot be used to make authenticated requests to the Shopify API once they expire.
|
127
|
+
|
128
|
+
Converting your app to use session tokens means that your app will most likely not go through the OAuth flow as often as it did when relying on cookie sessions. Since the online access tokens stored in your app's database are refreshed during OAuth, this may cause your app's user session repository to use expired online access tokens.
|
129
|
+
|
130
|
+
If the Shopify API returns `401 Unauthorized`, handle this error on your app by redirecting the user to your login path to start the OAuth flow. As a result, your app will be given a new online access token for the current user.
|
131
|
+
|
132
|
+
> **Note:** The following are examples to common app configurations. Your specific use-case may differ.
|
133
|
+
|
134
|
+
##### Example solution
|
135
|
+
|
136
|
+
Add the following line to your app's unauthorized response handler:
|
137
|
+
|
138
|
+
```diff
|
139
|
+
+ redirect_to(ShopifyApp.configuration.login_url, shop: current_shopify_domain)
|
140
|
+
```
|
141
|
+
|
142
|
+
_Example:_ If your embedded app cannot handle server-side XHR redirects, then configure your app's unauthorized response handler to set a response header:
|
143
|
+
|
144
|
+
```
|
145
|
+
X-Shopify-API-Request-Failure-Unauthorized: true
|
146
|
+
```
|
147
|
+
|
148
|
+
Then, use the [Shopify App Bridge Redirect](https://shopify.dev/tools/app-bridge/actions/navigation/redirect) action to redirect your app frontend to the app login URL if this header is set.
|
149
|
+
|
150
|
+
## Migrating to App Bridge 2.0
|
151
|
+
|
152
|
+
In order to upgrade your embedded app to the latest App Bridge 2.0 version, please refer to the [migration guide](https://shopify.dev/tutorials/migrate-your-app-to-app-bridge-2).
|
153
|
+
|
154
|
+
To ensure that your app's embedded layout doesn't import App Bridge 2.0 before fully migrating, make the following change to bind it to v1.x.
|
155
|
+
|
156
|
+
```diff
|
157
|
+
- <script src="https://unpkg.com/@shopify/app-bridge"></script>
|
158
|
+
+ <script src="https://unpkg.com/@shopify/app-bridge@1"></script>
|
159
|
+
```
|
data/docs/Upgrading.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Upgrading
|
2
|
+
|
3
|
+
This file documents important changes needed to upgrade your app's Shopify App version to a new major version.
|
4
|
+
|
5
|
+
#### Table of contents
|
6
|
+
|
7
|
+
[Upgrading to `v17.2.0`](#upgrading-to-v1720)
|
8
|
+
|
9
|
+
[Upgrading to `v13.0.0`](#upgrading-to-v1300)
|
10
|
+
|
11
|
+
[Upgrading to `v11.7.0`](#upgrading-to-v1170)
|
12
|
+
|
13
|
+
[Upgrading from `v8.6` to `v9.0.0`](#upgrading-from-v86-to-v900)
|
14
|
+
|
15
|
+
## Upgrading to `v17.2.0`
|
16
|
+
|
17
|
+
### Different SameSite cookie attribute behaviour
|
18
|
+
|
19
|
+
To support Rails `v6.1`, the [`SameSiteCookieMiddleware`](/lib/shopify_app/middleware/same_site_cookie_middleware.rb) was updated to configure cookies to `SameSite=None` if the app is embedded. Before this release, cookies were configured to `SameSite=None` only if this attribute had not previously been set before.
|
20
|
+
|
21
|
+
```diff
|
22
|
+
# same_site_cookie_middleware.rb
|
23
|
+
- cookie << '; SameSite=None' unless cookie =~ /;\s*samesite=/i
|
24
|
+
+ cookie << '; SameSite=None' if ShopifyApp.configuration.embedded_app?
|
25
|
+
```
|
26
|
+
|
27
|
+
By default, Rails `v6.1` configures `SameSite=Lax` on all cookies that don't specify this attribute.
|
28
|
+
|
29
|
+
## Upgrading to `v13.0.0`
|
30
|
+
|
31
|
+
Version 13.0.0 adds the ability to use both user and shop sessions, concurrently. This however involved a large
|
32
|
+
change to how session stores work. Here are the steps to migrate to 13.x
|
33
|
+
|
34
|
+
### Changes to `config/initializers/shopify_app.rb`
|
35
|
+
|
36
|
+
- *REMOVE* `config.per_user_tokens = [true|false]` this is no longer needed
|
37
|
+
- *CHANGE* `config.session_repository = 'Shop'` To `config.shop_session_repository = 'Shop'`
|
38
|
+
- *ADD (optional)* User Session Storage `config.user_session_repository = 'User'`
|
39
|
+
|
40
|
+
### Shop Model Changes (normally `app/models/shop.rb`)
|
41
|
+
|
42
|
+
- *CHANGE* `include ShopifyApp::SessionStorage` to `include ShopifyApp::ShopSessionStorage`
|
43
|
+
|
44
|
+
### Changes to the @shop_session instance variable (normally in `app/controllers/*.rb`)
|
45
|
+
|
46
|
+
- *CHANGE* if you are using shop sessions, `@shop_session` will need to be changed to `@current_shopify_session`.
|
47
|
+
|
48
|
+
### Changes to Rails `session`
|
49
|
+
|
50
|
+
- *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.
|
51
|
+
|
52
|
+
### Changes to `ShopifyApp::LoginProtection`
|
53
|
+
|
54
|
+
`ShopifyApp::LoginProtection`
|
55
|
+
|
56
|
+
- CHANGE if you are using `ShopifyApp::LoginProtection#shopify_session` in your code, it will need to be
|
57
|
+
changed to `ShopifyApp::LoginProtection#activate_shopify_session`
|
58
|
+
- CHANGE if you are using `ShopifyApp::LoginProtection#clear_shop_session` in your code, it will need to be
|
59
|
+
changed to `ShopifyApp::LoginProtection#clear_shopify_session`
|
60
|
+
|
61
|
+
### Notes
|
62
|
+
You do not need a user model; a shop session is fine for most applications.
|
63
|
+
|
64
|
+
---
|
65
|
+
|
66
|
+
## Upgrading to `v11.7.0`
|
67
|
+
|
68
|
+
### Session storage method signature breaking change
|
69
|
+
If you override `def self.store(auth_session)` method in your session storage model (e.g. Shop), the method signature has changed to `def self.store(auth_session, *args)` in order to support user-based token storage. Please update your method signature to include the second argument.
|
70
|
+
|
71
|
+
---
|
72
|
+
|
73
|
+
## Upgrading from `v8.6` to `v9.0.0`
|
74
|
+
|
75
|
+
### Configuration change
|
76
|
+
|
77
|
+
Add an API version configuration in `config/initializers/shopify_app.rb`
|
78
|
+
Set this to the version you want to run against by default. See [Shopify API docs](https://help.shopify.com/en/api/versioning) for versions available.
|
79
|
+
```ruby
|
80
|
+
config.api_version = '2019-04'
|
81
|
+
```
|
82
|
+
|
83
|
+
### Session storage change
|
84
|
+
|
85
|
+
You will need to add an `api_version` method to your session storage object. The default implementation for this is.
|
86
|
+
```ruby
|
87
|
+
def api_version
|
88
|
+
ShopifyApp.configuration.api_version
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
### Generated file change
|
93
|
+
|
94
|
+
`embedded_app.html.erb` the usage of `shop_session.url` needs to be changed to `shop_session.domain`
|
95
|
+
```erb
|
96
|
+
<script type="text/javascript">
|
97
|
+
ShopifyApp.init({
|
98
|
+
apiKey: "<%= ShopifyApp.configuration.api_key %>",
|
99
|
+
|
100
|
+
shopOrigin: "<%= "https://#{ @shop_session.url }" if @shop_session %>",
|
101
|
+
|
102
|
+
debug: false,
|
103
|
+
forceRedirect: true
|
104
|
+
});
|
105
|
+
</script>
|
106
|
+
```
|
107
|
+
is changed to
|
108
|
+
```erb
|
109
|
+
<script type="text/javascript">
|
110
|
+
ShopifyApp.init({
|
111
|
+
apiKey: "<%= ShopifyApp.configuration.api_key %>",
|
112
|
+
|
113
|
+
shopOrigin: "<%= "https://#{ @shop_session.domain }" if @shop_session %>",
|
114
|
+
|
115
|
+
debug: false,
|
116
|
+
forceRedirect: true
|
117
|
+
});
|
118
|
+
</script>
|
119
|
+
```
|
120
|
+
|
121
|
+
### ShopifyAPI changes
|
122
|
+
|
123
|
+
You will need to also follow the ShopifyAPI [upgrade guide](https://github.com/Shopify/shopify_api/blob/master/README.md#-breaking-change-notice-for-version-700-) to ensure your app is ready to work with API versioning.
|
124
|
+
|
125
|
+
[dashboard]:https://partners.shopify.com
|
126
|
+
[app-bridge]:https://help.shopify.com/en/api/embedded-apps/app-bridge
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Authentication
|
2
|
+
|
3
|
+
The Shopify App gem implements [OAuth 2.0](https://shopify.dev/tutorials/authenticate-with-oauth) to get [access tokens](https://shopify.dev/concepts/about-apis/authentication#api-access-modes). These are used to authenticate requests made by the app to the Shopify API.
|
4
|
+
|
5
|
+
By default, the gem generates an embedded app frontend that uses [Shopify App Bridge](https://shopify.dev/tools/app-bridge) to fetch [session tokens](https://shopify.dev/concepts/apps/building-embedded-apps-using-session-tokens). Session tokens are used by the embedded app to make authenticated requests to the app backend.
|
6
|
+
|
7
|
+
See [*Authenticate an embedded app using session tokens*](https://shopify.dev/tutorials/authenticate-your-app-using-session-tokens) to learn more.
|
8
|
+
|
9
|
+
> ⚠️ Be sure you understand the differences between the types of authentication schemes before reading this guide.
|
10
|
+
|
11
|
+
#### Table of contents
|
12
|
+
|
13
|
+
[OAuth callback](#oauth-callback)
|
14
|
+
|
15
|
+
[Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
|
16
|
+
|
17
|
+
[Rotate API credentials](#rotate-api-credentials)
|
18
|
+
|
19
|
+
[Available authentication mixins](#available-authentication-mixins)
|
20
|
+
* [`ShopifyApp::Authenticated`](#shopifyappauthenticated)
|
21
|
+
* [`ShopifyApp::EnsureAuthenticatedLinks`](#shopifyappensureauthenticatedlinks)
|
22
|
+
|
23
|
+
## OAuth callback
|
24
|
+
|
25
|
+
>️ **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.
|
26
|
+
|
27
|
+
Upon completing the OAuth flow, Shopify calls the app at the `callback_path`. 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:
|
28
|
+
* Logging into the shop and resetting the session
|
29
|
+
* [Installing Webhooks](/docs/shopify_app/webhooks.md)
|
30
|
+
* [Setting Scripttags](/docs/shopify_app/script-tags.md)
|
31
|
+
* [Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
|
32
|
+
* Redirecting to the return address
|
33
|
+
|
34
|
+
## Run jobs after the OAuth flow
|
35
|
+
|
36
|
+
See [`ShopifyApp::AfterAuthenticateJob`](/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb).
|
37
|
+
|
38
|
+
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:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
ShopifyApp.configure do |config|
|
42
|
+
config.after_authenticate_job = { job: "Shopify::AfterAuthenticateJob" }
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
The job can be configured as either a class or a class name string.
|
47
|
+
|
48
|
+
If you need the job to run synchronously add the `inline` flag:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
ShopifyApp.configure do |config|
|
52
|
+
config.after_authenticate_job = { job: Shopify::AfterAuthenticateJob, inline: true }
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
We've also provided a generator which creates a skeleton job and updates the initializer for you:
|
57
|
+
|
58
|
+
```
|
59
|
+
bin/rails g shopify_app:add_after_authenticate_job
|
60
|
+
```
|
61
|
+
|
62
|
+
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.
|
63
|
+
|
64
|
+
## Rotate API credentials
|
65
|
+
|
66
|
+
If your Shopify secret key is leaked, you can use the RotateShopifyTokenJob to perform [API Credential Rotation](https://help.shopify.com/en/api/getting-started/authentication/oauth/api-credential-rotation).
|
67
|
+
|
68
|
+
Before running the job, you'll need to generate a new secret key from your Shopify Partner dashboard, and update the `/config/initializers/shopify_app.rb` to hold your new and old secret keys:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
config.secret = Rails.application.secrets.shopify_secret
|
72
|
+
config.old_secret = Rails.application.secrets.old_shopify_secret
|
73
|
+
```
|
74
|
+
|
75
|
+
We've provided a generator which creates the job and an example rake task:
|
76
|
+
|
77
|
+
```sh
|
78
|
+
bin/rails g shopify_app:rotate_shopify_token_job
|
79
|
+
```
|
80
|
+
|
81
|
+
The generated rake task will be found at `lib/tasks/shopify/rotate_shopify_token.rake` and is provided strictly for example purposes. It might not work with your application out of the box without some configuration.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
|
85
|
+
```
|
86
|
+
|
87
|
+
> **Note:** If you are updating `shopify_app` from a version prior to 8.4.2 (and do not wish to run the default/install generator again), you will need to add [the following line](https://github.com/Shopify/shopify_app/blob/4f7e6cca2a472d8f7af44b938bd0fcafe4d8e88a/lib/generators/shopify_app/install/templates/shopify_provider.rb#L18) to `config/initializers/omniauth.rb`:
|
88
|
+
|
89
|
+
## Available authentication mixins
|
90
|
+
|
91
|
+
### `ShopifyApp::Authenticated`
|
92
|
+
|
93
|
+
The engine provides a [`ShopifyApp::Authenticated`](/app/controllers/concerns/shopify_app/authenticated.rb) concern which should be included in any controller that is intended to be behind Shopify OAuth. It adds `before_action`s to ensure that the user is authenticated and will redirect to the Shopify login page if not. It is best practice to include this concern in a base controller inheriting from your `ApplicationController`, from which all controllers that require Shopify authentication inherit.
|
94
|
+
|
95
|
+
*Example:*
|
96
|
+
|
97
|
+
```rb
|
98
|
+
class AuthenticatedController < ApplicationController
|
99
|
+
include ShopifyApp::Authenticated
|
100
|
+
end
|
101
|
+
|
102
|
+
class ApiController < AuthenticatedController
|
103
|
+
# Actions in this controller are protected
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
For backwards compatibility, the engine still provides a controller called `ShopifyApp::AuthenticatedController` which includes the `ShopifyApp::Authenticated` concern. Note that it inherits directly from `ActionController::Base`, so you will not be able to share functionality between it and your application's `ApplicationController`.
|
108
|
+
|
109
|
+
### `ShopifyApp::EnsureAuthenticatedLinks`
|
110
|
+
|
111
|
+
The [`ShopifyApp::EnsureAuthenticatedLinks`](/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb) concern helps authenticate users that access protected pages of your app directly.
|
112
|
+
|
113
|
+
Include this concern in your app's `AuthenticatedController` if your app uses session tokens with [Turbolinks](https://github.com/turbolinks/turbolinks). It adds a `before_action` filter that detects whether a session token is present or not. If a session token is not found, the user is redirected to your app's splash page path (`root_path`) along with `return_to` and `shop` parameters.
|
114
|
+
|
115
|
+
*Example:*
|
116
|
+
|
117
|
+
```rb
|
118
|
+
class AuthenticatedController < ApplicationController
|
119
|
+
include ShopifyApp::EnsureAuthenticatedLinks
|
120
|
+
include ShopifyApp::Authenticated
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
See [Authenticate server-side rendered embedded apps using Rails and Turbolinks](https://shopify.dev/tutorials/authenticate-server-side-rendered-embedded-apps-using-rails-and-turbolinks) for more information.
|