shopify_app 20.1.1 → 21.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/close-waiting-for-response-issues.yml +20 -0
  3. data/.github/workflows/remove-labels-on-activity.yml +16 -0
  4. data/.github/workflows/stale.yml +12 -6
  5. data/CHANGELOG.md +14 -0
  6. data/Gemfile.lock +7 -7
  7. data/README.md +13 -24
  8. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +1 -1
  9. data/app/controllers/shopify_app/webhooks_controller.rb +0 -2
  10. data/docs/Quickstart.md +14 -7
  11. data/docs/Troubleshooting.md +5 -10
  12. data/docs/Upgrading.md +7 -0
  13. data/docs/shopify_app/authentication.md +5 -1
  14. data/docs/shopify_app/content-security-policy.md +10 -0
  15. data/docs/shopify_app/session-repository.md +3 -3
  16. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +42 -38
  17. data/lib/shopify_app/access_scopes/user_strategy.rb +2 -3
  18. data/lib/shopify_app/controller_concerns/ensure_billing.rb +2 -13
  19. data/lib/shopify_app/controller_concerns/frame_ancestors.rb +2 -2
  20. data/lib/shopify_app/controller_concerns/login_protection.rb +2 -6
  21. data/lib/shopify_app/errors.rb +34 -0
  22. data/lib/shopify_app/managers/scripttags_manager.rb +1 -3
  23. data/lib/shopify_app/managers/webhooks_manager.rb +3 -4
  24. data/lib/shopify_app/session/in_memory_session_store.rb +1 -3
  25. data/lib/shopify_app/session/jwt.rb +8 -12
  26. data/lib/shopify_app/session/null_user_session_store.rb +1 -1
  27. data/lib/shopify_app/session/session_repository.rb +2 -3
  28. data/lib/shopify_app/session/user_session_storage.rb +1 -1
  29. data/lib/shopify_app/version.rb +1 -1
  30. data/lib/shopify_app.rb +3 -0
  31. data/package.json +1 -1
  32. data/shopify_app.gemspec +1 -1
  33. metadata +8 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87362d48113e64119d57d3f458fc3334f8097a7a234b9a77e806b327baa55196
4
- data.tar.gz: 07bd6f5051b5e77a6c360699fb6fa090e55e8d7674ba9814eadcf025a8566523
3
+ metadata.gz: 818a15b2d9822d6cf785722387873d805ca1709f9c4bec394e4481a0a7e8f70e
4
+ data.tar.gz: 0701e46e9492f2d387abbc1d2b2ef3c33a20b6196df340c353c6e6ddadc48cbe
5
5
  SHA512:
6
- metadata.gz: 750c1ffd57c2922165af74710a27e243b4a32fb016b393e22c8f861428700c754ebacc98e9c6604c0937df0a2197ed164444b1fe0bc9bab28c84b4d2be5ff2d0
7
- data.tar.gz: fc987464248ff4c0e88a91c9b5e8150659ef93f683a9912a3b1088e80fe50057bf6a821eb169a18d4e82e5a190d42abb82c10bcf6159a744831bdc07a7583a51
6
+ metadata.gz: 60f33ceb026c9684efbfe16d638f822a7ea82f9c4a24346a4ca3ddd6c525bfd6819913ea8fd3ac2c5aa7979d053dddf45fa5496c028d26f7a30e4dd2dd8d1b1c
7
+ data.tar.gz: b2c1fba3d38554e357d77e6c75348af9ade64e70e8aacb9bc9910a6e07c4eb8d69fa29e694abda00a25e7e836445a3f31eba2f8e88299f4d0a689ea70a99b7f7
@@ -0,0 +1,20 @@
1
+ name: Close Waiting for Response Issues
2
+ on:
3
+ schedule:
4
+ - cron: "30 1 * * *"
5
+ workflow_dispatch:
6
+ jobs:
7
+ check-need-info:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: close-issues
11
+ uses: actions-cool/issues-helper@v3
12
+ with:
13
+ actions: 'close-issues'
14
+ token: ${{ secrets.GITHUB_TOKEN }}
15
+ labels: 'Waiting for Response'
16
+ inactive-day: 7
17
+ body: |
18
+ We are closing this issue because we did not hear back regarding additional details we needed to resolve this issue. If the issue persists and you are able to provide the missing clarification we need, feel free to respond and reopen this issue.
19
+
20
+ We appreciate your understanding as we try to manage our number of open issues.
@@ -0,0 +1,16 @@
1
+ name: Remove Stale or Waiting Labels
2
+ on:
3
+ issue_comment:
4
+ types: [created]
5
+ workflow_dispatch:
6
+ jobs:
7
+ remove-labels-on-activity:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - uses: actions-ecosystem/action-remove-labels@v1
12
+ if: contains(github.event.issue.labels.*.name, 'Waiting for Response')
13
+ with:
14
+ labels: |
15
+ Waiting for Response
16
+
@@ -12,14 +12,20 @@ jobs:
12
12
  steps:
13
13
  - uses: actions/stale@v5
14
14
  with:
15
- days-before-issue-stale: 730
15
+ days-before-issue-stale: 90
16
16
  days-before-issue-close: 14
17
- stale-issue-label: "stale"
17
+ stale-issue-label: "Stale"
18
18
  stale-issue-message: >
19
- This issue is stale because it has been open for 2 years. It will be closed if no further action occurs in 14 days.
20
- close-issue-message: >
21
- This issue was closed because it has been inactive for 14 days since being marked as stale.
19
+ This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days.
20
+ close-issue-message: |
21
+ We are closing this issue because it has been inactive for a few months.
22
+ This probably means that it is not reproducible or it has been fixed in a newer version.
23
+ If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.
24
+
25
+ If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the [CONTRIBUTING.md](https://github.com/Shopify/shopify_app/blob/main/CONTRIBUTING.md) file for guidelines
26
+
27
+ Thank you!
22
28
  days-before-pr-stale: -1
23
29
  days-before-pr-close: -1
24
30
  repo-token: ${{ secrets.GITHUB_TOKEN }}
25
- operations_per_run: 100
31
+ exempt-issue-labels: "feature request"
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  Unreleased
2
2
  ----------
3
3
 
4
+ 21.0.0 (Oct 3, 2022)
5
+ ----------
6
+ * Updating shopify_api gem to 12.0.0
7
+
8
+ 20.2.0 (September 30, 2022)
9
+ ----------
10
+ * Fixes a method signature error bug when raising `BillingError`. [#1513](https://github.com/Shopify/shopify_app/pull/1513)
11
+ * Fixes bug with Rails 7 and import maps with Safari/Firefox on the HomeController#index view. [#1506](https://github.com/Shopify/shopify_app/pull/1506)
12
+ * Refactors how default `domain_host` is populated in the CSP header added to responses in the `FrameAncestors` controller concern. [#1504](https://github.com/Shopify/shopify_app/pull/1504)
13
+ * Removes duplicate `;` added in CSP header. [#1500](https://github.com/Shopify/shopify_app/pull/1500)
14
+
15
+ * Fixed an issue where `ShopifyApp::UserSessionStorage` was causing an infinite OAuth loop when not checking scopes. [#1516](https://github.com/Shopify/shopify_app/pull/1516)
16
+ * Move all error classes created for this gem into `lib/shopify_app/errors.rb`. Constant names of errors nested by modules and classes have been removed to give a shorter namespace.
17
+
4
18
  20.1.1 (September 2, 2022)
5
19
  ----------
6
20
 
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify_app (20.1.1)
4
+ shopify_app (21.0.0)
5
5
  activeresource
6
6
  browser_sniffer (~> 2.0)
7
7
  jwt (>= 2.2.3)
8
8
  rails (> 5.2.1)
9
9
  redirect_safely (~> 1.0)
10
- shopify_api (~> 11.1)
10
+ shopify_api (~> 12.0.0)
11
11
  sprockets-rails (>= 2.0.0)
12
12
 
13
13
  GEM
@@ -85,7 +85,7 @@ GEM
85
85
  ast (2.4.2)
86
86
  binding_of_caller (1.0.0)
87
87
  debug_inspector (>= 0.0.1)
88
- browser_sniffer (2.0.0)
88
+ browser_sniffer (2.1.0)
89
89
  builder (3.2.4)
90
90
  byebug (11.1.3)
91
91
  coderay (1.1.3)
@@ -125,7 +125,7 @@ GEM
125
125
  mini_portile2 (~> 2.8.0)
126
126
  racc (~> 1.4)
127
127
  oj (3.13.21)
128
- openssl (3.0.0)
128
+ openssl (3.0.1)
129
129
  parallel (1.21.0)
130
130
  parser (3.1.0.0)
131
131
  ast (~> 2.4.1)
@@ -194,7 +194,7 @@ GEM
194
194
  rubocop (~> 1.24)
195
195
  ruby-progressbar (1.11.0)
196
196
  securerandom (0.2.0)
197
- shopify_api (11.1.0)
197
+ shopify_api (12.0.0)
198
198
  concurrent-ruby
199
199
  hash_diff
200
200
  httparty
@@ -204,7 +204,7 @@ GEM
204
204
  securerandom
205
205
  sorbet-runtime
206
206
  zeitwerk (~> 2.5)
207
- sorbet-runtime (0.5.10398)
207
+ sorbet-runtime (0.5.10477)
208
208
  sprockets (4.1.1)
209
209
  concurrent-ruby (~> 1.0)
210
210
  rack (> 1, < 3)
@@ -224,7 +224,7 @@ GEM
224
224
  websocket-driver (0.7.5)
225
225
  websocket-extensions (>= 0.1.0)
226
226
  websocket-extensions (0.1.5)
227
- zeitwerk (2.6.0)
227
+ zeitwerk (2.6.1)
228
228
 
229
229
  PLATFORMS
230
230
  ruby
data/README.md CHANGED
@@ -32,41 +32,25 @@ This gem requires that you have the following credentials:
32
32
  - **Shopify API key:** The API key app credential specified in your [Shopify Partners dashboard](https://partners.shopify.com/organizations).
33
33
  - **Shopify API secret:** The API secret key app credential specified in your [Shopify Partners dashboard](https://partners.shopify.com/organizations).
34
34
 
35
- ### OAuth Tunnel in Development
36
-
37
- In order to redirect OAuth requests securely to localhost, you'll need to setup a tunnel to redirect from the internet to localhost.
38
-
39
- We've validated that [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/trycloudflare/) works with this template.
40
-
41
- To do that, you can [install the `cloudflared` CLI tool](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/), and run:
42
-
43
- ```shell
44
- # Note that you can also use a different port
45
- cloudflared tunnel --url http://localhost:3000
46
- ```
47
-
48
- You will need to keep this window running to maintain the tunnel during development.
49
-
50
35
  ## Usage
51
36
 
52
37
  1. To get started, create a new Rails app:
53
38
 
54
39
  ``` sh
55
- $ rails new my_shopify_app
40
+ rails new my_shopify_app
56
41
  ```
57
42
 
58
43
  2. Add the Shopify App gem to `my_shopify_app`'s Gemfile.
59
44
 
60
45
  ```sh
61
- $ bundle add shopify_app
46
+ bundle add shopify_app
62
47
  ```
63
48
 
64
49
  3. Create a `.env` file in the root of `my_shopify_app` to specify your Shopify API credentials:
65
50
 
66
- ```
51
+ ```sh
67
52
  SHOPIFY_API_KEY=<Your Shopify API key>
68
53
  SHOPIFY_API_SECRET=<Your Shopify API secret>
69
- HOST=<Your SSH tunnel host>
70
54
  ```
71
55
 
72
56
  > In a development environment, you can use a gem like `dotenv-rails` to manage environment variables.
@@ -74,22 +58,26 @@ HOST=<Your SSH tunnel host>
74
58
  4. Run the default Shopify App generator to create an app that can be embedded in the Shopify Admin:
75
59
 
76
60
  ```sh
77
- $ rails generate shopify_app
61
+ rails generate shopify_app
78
62
  ```
79
63
 
80
64
  5. Run a migration to create the necessary tables in your database:
81
65
 
82
66
  ```sh
83
- $ rails db:migrate
67
+ rails db:migrate
84
68
  ```
85
69
 
86
- 6. Run the app:
70
+ 6. Setup a SSH tunnel to allow the OAuth redirect to work. See how in the [Setup SSH tunnel for development](/docs/Quickstart.md#setup-ssh-tunnel-for-development) section in [Quickstart](/docs/Quickstart.md)
71
+
72
+ 7. Run the app:
87
73
 
88
74
  ```sh
89
- $ rails server
75
+ rails server
90
76
  ```
91
77
 
92
- See [*Quickstart*](/docs/Quickstart.md) to learn how to install your app on a shop.
78
+ 8. Install the app by visiting the server's URL (e.g. http://127.0.0.1:3000) and specifying the subdomain of the shop where you want it to be installed to.
79
+
80
+ 9. After the app is installed, you're redirected to the embedded app.
93
81
 
94
82
  This app implements [OAuth 2.0](https://shopify.dev/tutorials/authenticate-with-oauth) with Shopify to authenticate requests made to Shopify APIs. By default, this app is configured to use [session tokens](https://shopify.dev/concepts/apps/building-embedded-apps-using-session-tokens) to authenticate merchants when embedded in the Shopify Admin.
95
83
 
@@ -121,6 +109,7 @@ You can find documentation on gem usage, concepts, mixins, installation, and mor
121
109
  * [Handling changes in access scopes](/docs/shopify_app/handling-access-scopes-changes.md)
122
110
  * [Testing](/docs/shopify_app/testing.md)
123
111
  * [Webhooks](/docs/shopify_app/webhooks.md)
112
+ * [Content Security Policy](/docs/shopify_app/content-security-policy.md)
124
113
 
125
114
  ### Engine
126
115
 
@@ -26,7 +26,7 @@ module ShopifyApp
26
26
 
27
27
  def redirect_to_splash_page
28
28
  redirect_to(splash_page)
29
- rescue ShopifyApp::LoginProtection::ShopifyDomainNotFound => error
29
+ rescue ::ShopifyApp::ShopifyDomainNotFound => error
30
30
  Rails.logger.warn("[ShopifyApp::EnsureAuthenticatedLinks] Redirecting to login: [#{error.class}] "\
31
31
  "Could not determine current shop domain")
32
32
  redirect_to(ShopifyApp.configuration.login_url)
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyApp
4
- class MissingWebhookJobError < StandardError; end
5
-
6
4
  class WebhooksController < ActionController::Base
7
5
  include ShopifyApp::WebhookVerification
8
6
 
data/docs/Quickstart.md CHANGED
@@ -4,24 +4,31 @@ This guide assumes you have completed the steps to create a new Rails app using
4
4
 
5
5
  #### Table of contents
6
6
 
7
- [Make your app available to the internet](#make-your-app-available-to-the-internet)
7
+ [Setup SSH tunnel for development](#setup-ssh-tunnel-for-development)
8
8
 
9
9
  [Use Shopify App Bridge to embed your app in the Shopify Admin](#use-shopify-app-bridge-to-embed-your-app-in-the-shopify-admin)
10
10
 
11
- ## Make your app available to the internet
11
+ ## Setup SSH tunnel for development
12
12
 
13
13
  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](/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb) or receive [webhooks](/docs/shopify_app/webhooks.md).
14
14
 
15
- Use a tunneling service like [Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/trycloudflare/) to make your development environment accessible to the internet.
15
+ In order to receive requests securely, you'll need to setup a tunnel from the internet to localhost. You can use [Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/trycloudflare/) for this.
16
16
 
17
- To use Cloudflare, [install the `cloudflared` CLI tool](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/), and run:
17
+ To do so, [install the `cloudflared` CLI tool](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/), and run:
18
18
 
19
- ```shell
20
- # Note that you can also use a different port
19
+ ```sh
20
+ # The port must be the same as the one you run the Rails app on later. We use the Rails default below.
21
21
  cloudflared tunnel --url http://localhost:3000
22
22
  ```
23
23
 
24
- See the [*Embed the app in Shopify*](https://shopify.dev/tutorials/build-rails-react-app-that-uses-app-bridge-authentication#embed-the-app-in-shopify) section of [*Build a Shopify app with Rails, React, and App Bridge*](https://shopify.dev/tutorials/build-rails-react-app-that-uses-app-bridge-authentication) to learn more.
24
+ Keep this window running to keep the tunnel and make note of the URL this command prints out. The URL will look like `https://some-random-words.trycloudflare.com`.
25
+
26
+ Visit the "App Setup" section for your app in the [Shopify Partners dashboard](https://partners.shopify.com/organizations). Set the URL as "App URL" on this settings page and add it to the "Allowed redirection URL(s)", after appending `/auth/shopify/callback` to the end (e.g. `https://some-random-words.trycloudflare.com/auth/shopify/callback`).
27
+
28
+ Add the same URL as `HOST` in your `.env` file e.g.
29
+ ```sh
30
+ HOST='https://some-random-words.trycloudflare.com/'
31
+ ```
25
32
 
26
33
  ## Use Shopify App Bridge to embed your app in the Shopify Admin
27
34
 
@@ -16,8 +16,7 @@
16
16
  [JWT session tokens](#jwt-session-tokens)
17
17
  * [My app is still using cookies to authenticate](#my-app-is-still-using-cookies-to-authenticate)
18
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)
19
+ * [I'm stuck in a redirect loop after OAuth](#im-stuck-in-a-redirect-loop-after-oauth)
21
20
 
22
21
  ## Generators
23
22
 
@@ -26,7 +25,7 @@
26
25
  Rails uses spring by default to speed up development. To run the generator, spring has to be stopped:
27
26
 
28
27
  ```sh
29
- $ bundle exec spring stop
28
+ bundle exec spring stop
30
29
  ```
31
30
 
32
31
  Run shopify_app generator again.
@@ -144,13 +143,9 @@ X-Shopify-API-Request-Failure-Unauthorized: true
144
143
 
145
144
  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.
146
145
 
147
- ## Migrating to App Bridge 2.0
148
146
 
149
- 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).
147
+ ### I'm stuck in a redirect loop after OAuth
150
148
 
151
- 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.
149
+ In previous versions of `ShopifyApp::Authenticated` controller concern, App Bridge embedded apps were able to include the `Authenticated` controller concern in the `HomeController` and other embedded controllers. This is no longer supported due to browsers blocking 3rd party cookies to increase privacy. App Bridge 3 is needed to handle all embedded sessions.
152
150
 
153
- ```diff
154
- - <script src="https://unpkg.com/@shopify/app-bridge"></script>
155
- + <script src="https://unpkg.com/@shopify/app-bridge@1"></script>
156
- ```
151
+ For more details on how to handle embeded sessions, refer to [the session token documentation](https://shopify.dev/apps/auth/oauth/session-tokens).
data/docs/Upgrading.md CHANGED
@@ -4,6 +4,8 @@ This file documents important changes needed to upgrade your app's Shopify App v
4
4
 
5
5
  #### Table of contents
6
6
 
7
+ [Upgrading to `v20.2.0`](#upgrading-to-v2020)
8
+
7
9
  [Upgrading to `v20.1.0`](#upgrading-to-v2010)
8
10
 
9
11
  [Upgrading to `v19.0.0`](#upgrading-to-v1900)
@@ -18,6 +20,11 @@ This file documents important changes needed to upgrade your app's Shopify App v
18
20
 
19
21
  [Upgrading from `v8.6` to `v9.0.0`](#upgrading-from-v86-to-v900)
20
22
 
23
+ ## Upgrading to `v20.2.0`
24
+ All custom errors defined inline within the `ShopifyApp` gem have been moved to `lib/shopify_app/errors.rb`.
25
+
26
+ - If you rescue any errors defined in this gem, you will need to rename them to match their new namespacing.
27
+
21
28
  ## Upgrading to `v20.1.0`
22
29
 
23
30
  Note that the following steps are *optional* and only apply to **embedded** applications. However, they can improve the loading time of your embedded app at installation and re-auth.
@@ -106,6 +106,10 @@ end
106
106
 
107
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
108
 
109
+ #### Embedded apps and `ShopifyApp::Authenticated`
110
+
111
+ Embedded Shopify Admin apps can only use the `ShopifyApp::Authenticated` controller concern *if* the requests originate from App Bridge's `authenticatedFetch` method. Those who include this concern in the `HomeController` or some other embedded controller will see what looks like an OAuth redirect loop as the `ShopifyApp::Authenticated` concern will be fighting with the App Bridge. For more details on how to handle embedded sessions, refer to [the session token documentation](https://shopify.dev/apps/auth/oauth/session-tokens).
112
+
109
113
  ### `ShopifyApp::EnsureAuthenticatedLinks`
110
114
 
111
115
  The [`ShopifyApp::EnsureAuthenticatedLinks`](/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb) concern helps authenticate users that access protected pages of your app directly.
@@ -121,4 +125,4 @@ class AuthenticatedController < ApplicationController
121
125
  end
122
126
  ```
123
127
 
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.
128
+ 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.
@@ -0,0 +1,10 @@
1
+ # Content Security Policy Header
2
+
3
+ Shopify App [handles Rails' configuration](https://edgeguides.rubyonrails.org/security.html#content-security-policy-header) for [Content-Security-Policy Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) when the `ShopifyApp::FrameAncestors` controller concern is included in controllers. This is tyipcally done by including the [`ShopifyApp::Authenticated`](https://github.com/Shopify/shopify_app/blob/ed41165ca9598d2c9d514487365192f22b5eb096/app/controllers/concerns/shopify_app/authenticated.rb) controller concern rather that directly including it.
4
+
5
+ ## Included Domains
6
+
7
+ For actions that include the `ShopifyApp::FrameAncestors` controller concern, the following hosts are added to the Content-Security-Policy header as [per the store requirements](https://shopify.dev/apps/store/security/iframe-protection#embedded-apps):
8
+
9
+ 1. [`current_shopify_domain`](https://github.com/Shopify/shopify_app/blob/ed41165ca9598d2c9d514487365192f22b5eb096/app/controllers/concerns/shopify_app/require_known_shop.rb#L13) || `"*.myshopify.com"` if current shopify domain isn't present
10
+ 2. "https://admin.shopify.com"
@@ -20,7 +20,7 @@
20
20
 
21
21
  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.
22
22
  ```sh
23
- $ rails generate shopify_app:shop_model
23
+ rails generate shopify_app:shop_model
24
24
  ```
25
25
  This will generate a shop model which will be the storage for the tokens necessary for authentication.
26
26
 
@@ -28,8 +28,8 @@ This will generate a shop model which will be the storage for the tokens necessa
28
28
 
29
29
  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.
30
30
  ```sh
31
- $ rails generate shopify_app:shop_model
32
- $ rails generate shopify_app:user_model
31
+ rails generate shopify_app:shop_model
32
+ rails generate shopify_app:user_model
33
33
  ```
34
34
  This will generate a shop model and user model, which will be the storage for the tokens necessary for authentication.
35
35
 
@@ -7,48 +7,52 @@
7
7
  rel="stylesheet"
8
8
  href="https://unpkg.com/@shopify/polaris@4.25.0/styles.min.css"
9
9
  />
10
- <% if embedded_app? %> <script>
11
- document.addEventListener("DOMContentLoaded", async function() {
12
- <% if ShopifyApp.use_importmap? %>
13
- await import("lib/shopify_app")
14
- <% end %>
15
-
16
- var SessionToken = window["app-bridge"].actions.SessionToken
17
- var app = window.app;
10
+ <% if embedded_app? %>
11
+ <script type="module">
12
+ async function displayProducts() {
13
+ var SessionToken = window["app-bridge"].actions.SessionToken
14
+ var app = window.app;
18
15
 
19
- app.dispatch(
20
- SessionToken.request(),
21
- );
22
-
23
- // Save a session token for future requests
24
- window.sessionToken = await new Promise((resolve) => {
25
- app.subscribe(SessionToken.Action.RESPOND, (data) => {
26
- resolve(data.sessionToken || "");
16
+ app.dispatch(
17
+ SessionToken.request(),
18
+ );
19
+ // Save a session token for future requests
20
+ window.sessionToken = await new Promise((resolve) => {
21
+ app.subscribe(SessionToken.Action.RESPOND, (data) => {
22
+ resolve(data.sessionToken || "");
23
+ });
27
24
  });
28
- });
29
25
 
30
- var fetchProducts = function() {
31
- var headers = new Headers({ "Authorization": "Bearer " + window.sessionToken });
32
- return fetch("/products", { headers })
33
- .then(response => response.json())
34
- .then(data => {
35
- var products = data.products;
26
+ var fetchProducts = function() {
27
+ var headers = new Headers({ "Authorization": "Bearer " + window.sessionToken });
28
+ return fetch("/products", { headers })
29
+ .then(response => response.json())
30
+ .then(data => {
31
+ var products = data.products;
36
32
 
37
- if (products === undefined || products.length == 0) {
38
- document.getElementById("products").innerHTML = "<br>No products to display.";
39
- } else {
40
- var list = "";
41
- products.forEach((product) => {
42
- var link = `<a target="_top" href="https://<%%= @shop_origin %>/admin/products/${product.id}">`
43
- list += "<li>" + link + product.title + "</a></li>";
44
- });
45
- document.getElementById("products").innerHTML = "<ul>" + list + "</ul>";
46
- }
47
- });
48
- }();
49
- });
50
- </script>
51
- <% end %> </head>
33
+ if (products === undefined || products.length == 0) {
34
+ document.getElementById("products").innerHTML = "<br>No products to display.";
35
+ } else {
36
+ var list = "";
37
+ products.forEach((product) => {
38
+ var link = `<a target="_top" href="https://<%%= @shop_origin %>/admin/products/${product.id}">`
39
+ list += "<li>" + link + product.title + "</a></li>";
40
+ });
41
+ document.getElementById("products").innerHTML = "<ul>" + list + "</ul>";
42
+ }
43
+ });
44
+ }();
45
+ };
46
+
47
+ <% if ShopifyApp.use_importmap? %>
48
+ import "lib/shopify_app"
49
+ displayProducts();
50
+ <% else %>
51
+ document.addEventListener("DOMContentLoaded", displayProducts);
52
+ <% end %>
53
+ </script>
54
+ <% end %>
55
+ </head>
52
56
  <body>
53
57
  <h2>Products</h2>
54
58
  <% if embedded_app? %> <div id="products"><br>Loading...</div><% else %>
@@ -3,14 +3,13 @@
3
3
  module ShopifyApp
4
4
  module AccessScopes
5
5
  class UserStrategy
6
- class InvalidInput < StandardError; end
7
-
8
6
  class << self
9
7
  def update_access_scopes?(user_id: nil, shopify_user_id: nil)
10
8
  return update_access_scopes_for_user_id?(user_id) if user_id
11
9
  return update_access_scopes_for_shopify_user_id?(shopify_user_id) if shopify_user_id
12
10
 
13
- raise(InvalidInput, "#update_access_scopes? requires user_id or shopify_user_id parameter inputs")
11
+ raise(::ShopifyApp::InvalidInput,
12
+ "#update_access_scopes? requires user_id or shopify_user_id parameter inputs")
14
13
  end
15
14
 
16
15
  private
@@ -2,24 +2,13 @@
2
2
 
3
3
  module ShopifyApp
4
4
  module EnsureBilling
5
- class BillingError < StandardError
6
- attr_accessor :message
7
- attr_accessor :errors
8
-
9
- def initialize(message, errors)
10
- super
11
- @message = message
12
- @errors = errors
13
- end
14
- end
15
-
16
5
  extend ActiveSupport::Concern
17
6
 
18
7
  RECURRING_INTERVALS = [BillingConfiguration::INTERVAL_EVERY_30_DAYS, BillingConfiguration::INTERVAL_ANNUAL]
19
8
 
20
9
  included do
21
10
  before_action :check_billing, if: :billing_required?
22
- rescue_from BillingError, with: :handle_billing_error
11
+ rescue_from ::ShopifyApp::BillingError, with: :handle_billing_error
23
12
  end
24
13
 
25
14
  private
@@ -119,7 +108,7 @@ module ShopifyApp
119
108
  data = data["data"]["appPurchaseOneTimeCreate"]
120
109
  end
121
110
 
122
- raise BillingError.new("Error while billing the store", data["userErrros"]) unless data["userErrors"].empty?
111
+ raise BillingError.new("Error while billing the store", data["userErrors"]) unless data["userErrors"].empty?
123
112
 
124
113
  data["confirmationUrl"]
125
114
  end
@@ -7,8 +7,8 @@ module ShopifyApp
7
7
  included do
8
8
  content_security_policy do |policy|
9
9
  policy.frame_ancestors(-> do
10
- domain_host = current_shopify_domain || "*.myshopify.com"
11
- "https://#{domain_host} https://admin.shopify.com;"
10
+ domain_host = current_shopify_domain || "*.#{::ShopifyApp.configuration.myshopify_domain}"
11
+ "https://#{domain_host} https://admin.shopify.com"
12
12
  end)
13
13
  end
14
14
  end
@@ -8,10 +8,6 @@ module ShopifyApp
8
8
  include ShopifyApp::Itp
9
9
  include ShopifyApp::SanitizedParams
10
10
 
11
- class ShopifyDomainNotFound < StandardError; end
12
-
13
- class ShopifyHostNotFound < StandardError; end
14
-
15
11
  included do
16
12
  after_action :set_test_cookie
17
13
  rescue_from ShopifyAPI::Errors::HttpResponseError, with: :handle_http_error
@@ -194,12 +190,12 @@ module ShopifyApp
194
190
 
195
191
  return shopify_domain if shopify_domain.present?
196
192
 
197
- raise ShopifyDomainNotFound
193
+ raise ::ShopifyApp::ShopifyDomainNotFound
198
194
  end
199
195
 
200
196
  def return_address
201
197
  return_address_with_params(shop: current_shopify_domain, host: host)
202
- rescue ShopifyDomainNotFound, ShopifyHostNotFound
198
+ rescue ::ShopifyApp::ShopifyDomainNotFound, ::ShopifyApp::ShopifyHostNotFound
203
199
  base_return_address
204
200
  end
205
201
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ class BillingError < StandardError
5
+ attr_accessor :message
6
+ attr_accessor :errors
7
+
8
+ def initialize(message, errors)
9
+ super(message)
10
+ @message = message
11
+ @errors = errors
12
+ end
13
+ end
14
+
15
+ class ConfigurationError < StandardError; end
16
+
17
+ class CreationFailed < StandardError; end
18
+
19
+ class EnvironmentError < StandardError; end
20
+
21
+ class InvalidAudienceError < StandardError; end
22
+
23
+ class InvalidDestinationError < StandardError; end
24
+
25
+ class InvalidInput < StandardError; end
26
+
27
+ class MismatchedHostsError < StandardError; end
28
+
29
+ class MissingWebhookJobError < StandardError; end
30
+
31
+ class ShopifyDomainNotFound < StandardError; end
32
+
33
+ class ShopifyHostNotFound < StandardError; end
34
+ end
@@ -2,8 +2,6 @@
2
2
 
3
3
  module ShopifyApp
4
4
  class ScripttagsManager
5
- class CreationFailed < StandardError; end
6
-
7
5
  def self.queue(shop_domain, shop_token, scripttags)
8
6
  ShopifyApp::ScripttagsManagerJob.perform_later(
9
7
  shop_domain: shop_domain,
@@ -69,7 +67,7 @@ module ShopifyApp
69
67
  begin
70
68
  scripttag.save!
71
69
  rescue ShopifyAPI::Errors::HttpResponseError => e
72
- raise CreationFailed, e.message
70
+ raise ::ShopifyApp::CreationFailed, e.message
73
71
  end
74
72
 
75
73
  scripttag
@@ -4,8 +4,6 @@ require "uri"
4
4
 
5
5
  module ShopifyApp
6
6
  class WebhooksManager
7
- class CreationFailed < StandardError; end
8
-
9
7
  class << self
10
8
  def queue(shop_domain, shop_token)
11
9
  ShopifyApp::WebhooksManagerJob.perform_later(
@@ -64,12 +62,13 @@ module ShopifyApp
64
62
  elsif uri&.path&.present?
65
63
  uri.path
66
64
  else
67
- raise ShopifyApp::MissingWebhookJobError, "The :path attribute is required for webhook registration."
65
+ raise ::ShopifyApp::MissingWebhookJobError,
66
+ "The :path attribute is required for webhook registration."
68
67
  end
69
68
  end
70
69
 
71
70
  def webhook_job_klass(path)
72
- webhook_job_klass_name(path).safe_constantize || raise(ShopifyApp::MissingWebhookJobError)
71
+ webhook_job_klass_name(path).safe_constantize || raise(::ShopifyApp::MissingWebhookJobError)
73
72
  end
74
73
 
75
74
  def webhook_job_klass_name(path)
@@ -4,8 +4,6 @@ module ShopifyApp
4
4
  # rubocop:disable Style/ClassVars
5
5
  # Class var repo is needed here in order to share data between the 2 child classes.
6
6
  class InMemorySessionStore
7
- class EnvironmentError < StandardError; end
8
-
9
7
  def self.retrieve(id)
10
8
  repo[id]
11
9
  end
@@ -22,7 +20,7 @@ module ShopifyApp
22
20
 
23
21
  def self.repo
24
22
  if Rails.env.production?
25
- raise EnvironmentError, "Cannot use InMemorySessionStore in a Production environment. \
23
+ raise ::ShopifyApp::EnvironmentError, "Cannot use InMemorySessionStore in a Production environment. \
26
24
  Please initialize ShopifyApp with a model that can store and retrieve sessions"
27
25
  end
28
26
  @@repo ||= {}
@@ -2,20 +2,14 @@
2
2
 
3
3
  module ShopifyApp
4
4
  class JWT
5
- class InvalidDestinationError < StandardError; end
6
-
7
- class MismatchedHostsError < StandardError; end
8
-
9
- class InvalidAudienceError < StandardError; end
10
-
11
5
  WARN_EXCEPTIONS = [
12
6
  ::JWT::DecodeError,
13
7
  ::JWT::ExpiredSignature,
14
8
  ::JWT::ImmatureSignature,
15
9
  ::JWT::VerificationError,
16
- InvalidAudienceError,
17
- InvalidDestinationError,
18
- MismatchedHostsError,
10
+ ::ShopifyApp::InvalidAudienceError,
11
+ ::ShopifyApp::InvalidDestinationError,
12
+ ::ShopifyApp::MismatchedHostsError,
19
13
  ]
20
14
 
21
15
  def initialize(token)
@@ -58,9 +52,11 @@ module ShopifyApp
58
52
  iss_host = ShopifyApp::Utils.sanitize_shop_domain(payload["iss"])
59
53
  api_key = ShopifyApp.configuration.api_key
60
54
 
61
- raise InvalidAudienceError, "'aud' claim does not match api_key" unless payload["aud"] == api_key
62
- raise InvalidDestinationError, "'dest' claim host not a valid shopify host" unless dest_host
63
- raise MismatchedHostsError, "'dest' claim host does not match 'iss' claim host" unless dest_host == iss_host
55
+ raise ::ShopifyApp::InvalidAudienceError,
56
+ "'aud' claim does not match api_key" unless payload["aud"] == api_key
57
+ raise ::ShopifyApp::InvalidDestinationError, "'dest' claim host not a valid shopify host" unless dest_host
58
+ raise ::ShopifyApp::MismatchedHostsError,
59
+ "'dest' claim host does not match 'iss' claim host" unless dest_host == iss_host
64
60
 
65
61
  payload
66
62
  end
@@ -8,7 +8,7 @@ module ShopifyApp
8
8
  end
9
9
 
10
10
  def store(_, _)
11
- raise SessionRepository::ConfigurationError, "user_storage is not configured"
11
+ raise ::ShopifyApp::ConfigurationError, "user_storage is not configured"
12
12
  end
13
13
 
14
14
  def retrieve_by_shopify_user_id(_)
@@ -4,8 +4,6 @@ module ShopifyApp
4
4
  class SessionRepository
5
5
  extend ShopifyAPI::Auth::SessionStorage
6
6
 
7
- class ConfigurationError < StandardError; end
8
-
9
7
  class << self
10
8
  attr_writer :shop_storage
11
9
 
@@ -36,7 +34,8 @@ module ShopifyApp
36
34
  end
37
35
 
38
36
  def shop_storage
39
- load_shop_storage || raise(ConfigurationError, "ShopifySessionRepository.shop_storage is not configured!")
37
+ load_shop_storage || raise(::ShopifyApp::ConfigurationError,
38
+ "ShopifySessionRepository.shop_storage is not configured!")
40
39
  end
41
40
 
42
41
  def user_storage
@@ -11,7 +11,7 @@ module ShopifyApp
11
11
 
12
12
  class_methods do
13
13
  def store(auth_session, user)
14
- user = find_or_initialize_by(shopify_user_id: user[:id])
14
+ user = find_or_initialize_by(shopify_user_id: user.id)
15
15
  user.shopify_token = auth_session.access_token
16
16
  user.shopify_domain = auth_session.shop
17
17
  user.save!
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyApp
4
- VERSION = "20.1.1"
4
+ VERSION = "21.0.0"
5
5
  end
data/lib/shopify_app.rb CHANGED
@@ -34,6 +34,9 @@ module ShopifyApp
34
34
  # utils
35
35
  require "shopify_app/utils"
36
36
 
37
+ # errors
38
+ require "shopify_app/errors"
39
+
37
40
  # controller concerns
38
41
  require "shopify_app/controller_concerns/csrf_protection"
39
42
  require "shopify_app/controller_concerns/localization"
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shopify_app",
3
- "version": "20.1.1",
3
+ "version": "21.0.0",
4
4
  "repository": "git@github.com:Shopify/shopify_app.git",
5
5
  "author": "Shopify",
6
6
  "license": "MIT",
data/shopify_app.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_runtime_dependency("jwt", ">= 2.2.3")
20
20
  s.add_runtime_dependency("rails", "> 5.2.1")
21
21
  s.add_runtime_dependency("redirect_safely", "~> 1.0")
22
- s.add_runtime_dependency("shopify_api", "~> 11.1")
22
+ s.add_runtime_dependency("shopify_api", "~> 12.0.0")
23
23
  s.add_runtime_dependency("sprockets-rails", ">= 2.0.0")
24
24
 
25
25
  s.add_development_dependency("byebug")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_app
3
3
  version: !ruby/object:Gem::Version
4
- version: 20.1.1
4
+ version: 21.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-02 00:00:00.000000000 Z
11
+ date: 2022-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeresource
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '11.1'
89
+ version: 12.0.0
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '11.1'
96
+ version: 12.0.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: sprockets-rails
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -262,7 +262,9 @@ files:
262
262
  - ".github/PULL_REQUEST_TEMPLATE.md"
263
263
  - ".github/workflows/build.yml"
264
264
  - ".github/workflows/cla.yml"
265
+ - ".github/workflows/close-waiting-for-response-issues.yml"
265
266
  - ".github/workflows/release.yml"
267
+ - ".github/workflows/remove-labels-on-activity.yml"
266
268
  - ".github/workflows/rubocop.yml"
267
269
  - ".github/workflows/stale.yml"
268
270
  - ".gitignore"
@@ -339,6 +341,7 @@ files:
339
341
  - docs/Troubleshooting.md
340
342
  - docs/Upgrading.md
341
343
  - docs/shopify_app/authentication.md
344
+ - docs/shopify_app/content-security-policy.md
342
345
  - docs/shopify_app/engine.md
343
346
  - docs/shopify_app/generators.md
344
347
  - docs/shopify_app/handling-access-scopes-changes.md
@@ -411,6 +414,7 @@ files:
411
414
  - lib/shopify_app/controller_concerns/sanitized_params.rb
412
415
  - lib/shopify_app/controller_concerns/webhook_verification.rb
413
416
  - lib/shopify_app/engine.rb
417
+ - lib/shopify_app/errors.rb
414
418
  - lib/shopify_app/jobs/scripttags_manager_job.rb
415
419
  - lib/shopify_app/jobs/webhooks_manager_job.rb
416
420
  - lib/shopify_app/managers/scripttags_manager.rb