shopify_app 22.5.2 → 23.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yaml +6 -0
  3. data/.github/workflows/build.yml +5 -5
  4. data/.github/workflows/close-waiting-for-response-issues.yml +1 -1
  5. data/.github/workflows/release.yml +2 -2
  6. data/.github/workflows/remove-labels-on-activity.yml +2 -3
  7. data/.github/workflows/rubocop.yml +2 -2
  8. data/CHANGELOG.md +22 -0
  9. data/Gemfile +1 -1
  10. data/Gemfile.lock +181 -122
  11. data/README.md +105 -25
  12. data/Rakefile +13 -0
  13. data/app/controllers/shopify_app/callback_controller.rb +2 -40
  14. data/app/controllers/shopify_app/extension_verification_controller.rb +1 -1
  15. data/app/jobs/shopify_app/script_tags_manager_job.rb +16 -0
  16. data/docs/Releasing.md +113 -19
  17. data/docs/Upgrading.md +72 -0
  18. data/docs/shopify_app/content-security-policy.md +50 -3
  19. data/docs/shopify_app/controller-concerns.md +20 -0
  20. data/docs/shopify_app/script-tags.md +52 -0
  21. data/docs/shopify_app/sessions.md +149 -22
  22. data/lib/generators/shopify_app/add_app_uninstalled_job/templates/app_uninstalled_job.rb.tt +2 -2
  23. data/lib/generators/shopify_app/add_privacy_jobs/templates/customers_data_request_job.rb.tt +2 -2
  24. data/lib/generators/shopify_app/add_privacy_jobs/templates/customers_redact_job.rb.tt +2 -2
  25. data/lib/generators/shopify_app/add_privacy_jobs/templates/shop_redact_job.rb.tt +2 -2
  26. data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +2 -2
  27. data/lib/generators/shopify_app/install/install_generator.rb +1 -1
  28. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +18 -0
  29. data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_token_expiry_columns.erb +7 -0
  30. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -1
  31. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -1
  32. data/lib/shopify_app/auth/post_authenticate_tasks.rb +8 -0
  33. data/lib/shopify_app/auth/token_exchange.rb +7 -0
  34. data/lib/shopify_app/configuration.rb +5 -5
  35. data/lib/shopify_app/controller_concerns/login_protection.rb +12 -8
  36. data/lib/shopify_app/engine.rb +2 -5
  37. data/lib/shopify_app/errors.rb +2 -0
  38. data/lib/shopify_app/managers/script_tags_manager.rb +348 -0
  39. data/lib/shopify_app/session/shop_session_storage.rb +84 -2
  40. data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +6 -0
  41. data/lib/shopify_app/session/user_session_storage.rb +21 -2
  42. data/lib/shopify_app/session/user_session_storage_with_scopes.rb +6 -0
  43. data/lib/shopify_app/utils.rb +1 -1
  44. data/lib/shopify_app/version.rb +1 -1
  45. data/lib/shopify_app.rb +12 -7
  46. data/package.json +1 -1
  47. data/shopify_app.gemspec +9 -10
  48. data/translation.yml +1 -0
  49. data/yarn.lock +7 -9
  50. metadata +63 -46
  51. data/lib/shopify_app/middleware/jwt_middleware.rb +0 -48
  52. data/lib/shopify_app/session/jwt.rb +0 -73
  53. /data/{lib/shopify_app/jobs → app/jobs/shopify_app}/webhooks_manager_job.rb +0 -0
data/README.md CHANGED
@@ -31,6 +31,12 @@ This gem requires that you have the following credentials:
31
31
 
32
32
  ## Usage
33
33
 
34
+ > [!NOTE]
35
+ > This package works best with the Shopify CLI. Learn more at https://shopify.dev/docs/apps/build/cli-for-apps
36
+ >
37
+ > To get started with a Rails app already connected to the Shopify CLI, run `shopify app init --template=ruby` for a complete setup app. (Recommended)
38
+ > Follow the instructions below to use this gem with a new or existing Rails app.
39
+
34
40
  1. To get started, create a new Rails app:
35
41
 
36
42
  ``` sh
@@ -43,44 +49,120 @@ rails new my_shopify_app
43
49
  bundle add shopify_app
44
50
  ```
45
51
 
46
- 3. You will need to provide several environment variables to the app.
47
- There are a variety of way of doing this, but for a development environment we recommended the [`dotenv-rails`](https://github.com/bkeepers/dotenv) gem.
48
- Create a `.env` file in the root of your Rails app to specify the full host and Shopify API credentials:
52
+ 3. Connect the Shopify CLI to your app:
49
53
 
50
- ```sh
51
- HOST=http://localhost:3000
52
- SHOPIFY_API_KEY=<Your Shopify API key>
53
- SHOPIFY_API_SECRET=<Your Shopify API secret>
54
+ ### Setting up Shopify CLI
55
+
56
+ For a more detailed guide on how to set up Shopify CLI, see the [Shopify CLI documentation](https://shopify.dev/docs/apps/build/cli-for-apps/app-structure).
57
+
58
+ To use your Rails app with Shopify CLI, you'll need to create the required configuration files and set up your project structure:
59
+
60
+ 1. Create a `package.json` file in your app's root directory:
61
+
62
+ ```json
63
+ {
64
+ "name": "my-shopify-app",
65
+ "version": "1.0.0",
66
+ "description": "Shopify app built with Rails",
67
+ "scripts": {},
68
+ "dependencies": {},
69
+ "engines": {
70
+ "node": ">=18.0.0"
71
+ }
72
+ }
54
73
  ```
74
+ This is required for the Shopify CLI to work. It will be used if you add extension like Checkout UI [extensions](https://shopify.dev/docs/api/checkout-extensions) to your app.
55
75
 
56
- 4. Run the default Shopify App generator to create an app that can be embedded in the Shopify Admin:
76
+ 2. Create a `shopify.app.toml` configuration file in your app's root:
57
77
 
58
- ```sh
59
- rails generate shopify_app
78
+ ```toml
79
+ # Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
80
+
81
+ client_id = ""
82
+ name = "My Shopify App"
83
+ handle = "my-shopify-app"
84
+ application_url = "https://localhost:3000"
85
+ embedded = true
86
+
87
+ [access_scopes]
88
+ # Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
89
+ scopes = "write_products"
90
+
91
+ [auth]
92
+ redirect_urls = [
93
+ "https://localhost:3000/api/auth",
94
+ "https://localhost:3000/api/auth/callback",
95
+ "https://localhost:3000/auth/callback",
96
+ "https://localhost:3000/auth/shopify/callback"
97
+ ]
98
+
99
+ [webhooks]
100
+ api_version = "2024-10"
101
+
102
+ [[webhooks.subscriptions]]
103
+ topics = ["app/uninstalled"]
104
+ uri = "/api/webhooks"
105
+
106
+ [pos]
107
+ embedded = false
108
+
109
+ [build]
110
+ automatically_update_urls_on_dev = true
111
+ dev_store_url = "YOUR-DEV-STORE.myshopify.com"
60
112
  ```
61
113
 
62
- 5. Run a migration to create the necessary tables in your database:
114
+ 3. Create a `shopify.web.toml` file to configure how Shopify CLI runs your Rails app:
63
115
 
64
- ```sh
65
- rails db:migrate
116
+ ```toml
117
+ # Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
118
+
119
+ [commands]
120
+ dev = "bin/rails server -b 0.0.0.0 -e development"
121
+ build = "RAILS_ENV=production bin/rails assets:precompile"
66
122
  ```
67
123
 
68
- 6. Run the app:
124
+ 4. (Optional) Update your Rails development configuration to allow Shopify's tunnel hosts. In `config/environments/development.rb`, add:
69
125
 
70
- ```sh
71
- rails server
126
+ ```ruby
127
+ Rails.application.configure do
128
+ # Allow ngrok tunnels for secure Shopify OAuth redirects
129
+ config.hosts = (config.hosts rescue []) << /[-\w]+\.ngrok\.io/
130
+ # Allow Cloudflare tunnels for secure Shopify OAuth redirects
131
+ config.hosts = (config.hosts rescue []) << /[-\w]+\.trycloudflare\.com/
132
+
133
+ # Or to allow all hosts in development (less secure):
134
+ # config.hosts.clear
135
+
136
+ # ... rest of your configuration
137
+ end
72
138
  ```
139
+ With the Shopify CLI you can use [localhost development](https://shopify.dev/docs/apps/build/cli-for-apps/development) to run your app.
73
140
 
74
- 7. Within [Shopify Partners](https://www.shopify.com/partners), navigate to your App, then App Setup, and configure the URLs, e.g.:
141
+ 5. Generate your API credentials
142
+ ```
143
+ shopify app config link
144
+ > create a new app
145
+ ```
75
146
 
76
- * App URL: http://localhost:3000/
77
- * Allowed redirection URL(s): http://localhost:3000/auth/shopify/callback
147
+ 6. Run the default Shopify App generator to create an app that can be embedded in the Shopify Admin:
78
148
 
79
- 8. Install the app by visiting the server's URL (e.g. http://localhost:3000) and specifying the subdomain of the shop where you want it to be installed to.
149
+ ```sh
150
+ rails generate shopify_app
151
+ ```
152
+
153
+ 7. Run a migration to create the necessary tables in your database:
154
+
155
+ ```sh
156
+ rails db:migrate
157
+ ```
80
158
 
81
- 9. After the app is installed, you're redirected to the embedded app.
159
+ 8. Start the dev server and install the app
160
+ ```
161
+ shopify app dev
162
+ ```
163
+ Clink on the Preview URL to install the app and view it in the Shopify Admin
82
164
 
83
- 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.
165
+ This app implements [OAuth 2.0 Token Exchange](https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/token-exchange) 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.
84
166
 
85
167
  See [*Generators*](/docs/shopify_app/generators.md) for a complete list of generators available to Shopify App.
86
168
 
@@ -107,7 +189,6 @@ You can find documentation on gem usage, concepts, mixins, installation, and mor
107
189
  * [Controller Concerns](/docs/shopify_app/controller-concerns.md)
108
190
  * [Generators](/docs/shopify_app/generators.md)
109
191
  * [Sessions](/docs/shopify_app/sessions.md)
110
- * [Handling changes in access scopes](/docs/shopify_app/handling-access-scopes-changes.md)
111
192
  * [Testing](/docs/shopify_app/testing.md)
112
193
  * [Webhooks](/docs/shopify_app/webhooks.md)
113
194
  * [Content Security Policy](/docs/shopify_app/content-security-policy.md)
@@ -161,8 +242,7 @@ ShopifyApp.configure do |config|
161
242
  config.embedded_app = true
162
243
  config.new_embedded_auth_strategy = true
163
244
 
164
- # If your app is configured to use online sessions, you can enable session expiry date check so a new access token
165
- # is fetched automatically when the session expires.
245
+ # You can enable session expiry date check so a new access token is fetched automatically when the session expires.
166
246
  # See expiry date check docs: https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/sessions.md#expiry-date
167
247
  config.check_session_expiry_date = true
168
248
  ...
data/Rakefile CHANGED
@@ -6,3 +6,16 @@ require "rake/testtask"
6
6
  require File.expand_path("../test/dummy/config/application", __FILE__)
7
7
 
8
8
  Rails.application.load_tasks
9
+
10
+ # Clear Rails' test task which expects bin/rails to exist.
11
+ # This gem uses a dummy Rails app for testing but doesn't have bin/rails
12
+ # since it's a gem, not a standalone Rails application.
13
+ Rake::Task["test"].clear if Rake::Task.task_defined?("test")
14
+
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.libs << "test"
17
+ t.pattern = "test/**/*_test.rb"
18
+ t.verbose = true
19
+ end
20
+
21
+ task default: :test
@@ -23,16 +23,8 @@ module ShopifyApp
23
23
 
24
24
  return respond_with_user_token_flow if start_user_token_flow?(api_session)
25
25
 
26
- if ShopifyApp::VERSION < "23.0"
27
- # deprecated in 23.0
28
- if ShopifyApp.configuration.custom_post_authenticate_tasks.present?
29
- ShopifyApp.configuration.post_authenticate_tasks.perform(api_session)
30
- else
31
- perform_post_authenticate_jobs(api_session)
32
- end
33
- else
34
- ShopifyApp.configuration.post_authenticate_tasks.perform(api_session)
35
- end
26
+ ShopifyApp.configuration.post_authenticate_tasks.perform(api_session)
27
+
36
28
  redirect_to_app if check_billing(api_session)
37
29
  end
38
30
 
@@ -145,35 +137,5 @@ module ShopifyApp
145
137
  def user_access_scopes_strategy
146
138
  ShopifyApp.configuration.user_access_scopes_strategy
147
139
  end
148
-
149
- def perform_post_authenticate_jobs(session)
150
- # Ensure we use the shop session to install webhooks
151
- session_for_shop = session.online? ? shop_session : session
152
-
153
- install_webhooks(session_for_shop)
154
-
155
- perform_after_authenticate_job(session)
156
- end
157
-
158
- def install_webhooks(session)
159
- return unless ShopifyApp.configuration.has_webhooks?
160
-
161
- WebhooksManager.queue(session.shop, session.access_token)
162
- end
163
-
164
- def perform_after_authenticate_job(session)
165
- config = ShopifyApp.configuration.after_authenticate_job
166
-
167
- return unless config && config[:job].present?
168
-
169
- job = config[:job]
170
- job = job.constantize if job.is_a?(String)
171
-
172
- if config[:inline] == true
173
- job.perform_now(shop_domain: session.shop)
174
- else
175
- job.perform_later(shop_domain: session.shop)
176
- end
177
- end
178
140
  end
179
141
  end
@@ -9,7 +9,7 @@ module ShopifyApp
9
9
  private
10
10
 
11
11
  def verify_request
12
- unless hmac_valid?(request.body.read)
12
+ unless hmac_valid?(request.raw_post)
13
13
  head(:unauthorized)
14
14
  ShopifyApp::Logger.debug("Extension verification failed due to invalid HMAC")
15
15
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ class ScriptTagsManagerJob < ActiveJob::Base
5
+ queue_as do
6
+ ShopifyApp.configuration.script_tags_manager_queue_name
7
+ end
8
+
9
+ def perform(shop_domain:, shop_token:, script_tags:)
10
+ ShopifyAPI::Auth::Session.temp(shop: shop_domain, access_token: shop_token) do |session|
11
+ manager = ScriptTagsManager.new(script_tags, shop_domain)
12
+ manager.create_script_tags(session: session)
13
+ end
14
+ end
15
+ end
16
+ end
data/docs/Releasing.md CHANGED
@@ -1,21 +1,115 @@
1
1
  # Releasing ShopifyApp
2
2
 
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 origin vX.Y.Z
16
- ```
17
- 1. Check that Create Release workflow successfully runs
18
- 1. Use Shipit to build and push the gem
19
-
20
- If you see an error like 'You need to create the vX.Y.X tag first', clear git
21
- cache in Shipit settings
3
+ ## Prerequisites
4
+
5
+ Before starting a release, ensure you have:
6
+
7
+ - Merged all code change PRs into main/master
8
+ - Access to publish the gem via Shipit
9
+
10
+ ## Release Process
11
+
12
+ ### Step 1: Prepare the Release Branch
13
+
14
+ 1. **Ensure all feature changes are merged**
15
+
16
+ ```bash
17
+ git checkout main
18
+ git pull origin main
19
+ ```
20
+
21
+ Verify: Latest commits should match GitHub's main branch.
22
+
23
+ 2. **Review changes to determine version bump**
24
+ - Apply [semantic versioning](https://semver.org/):
25
+ - PATCH (X.Y.Z+1): Bug fixes only
26
+ - MINOR (X.Y+1.0): New features, backward compatible
27
+ - MAJOR (X+1.0.0): Breaking changes
28
+
29
+ ### Step 2: Create Release Pull Request
30
+
31
+ 1. **Create a new branch**
32
+
33
+ ```bash
34
+ git checkout -b vX.Y.Z
35
+ ```
36
+
37
+ 2. **Update version numbers**
38
+
39
+ Edit `lib/shopify_app/version.rb`:
40
+ ```ruby
41
+ module ShopifyApp
42
+ VERSION = "X.Y.Z" # Replace with your version
43
+ end
44
+ ```
45
+
46
+ Edit `package.json`:
47
+ ```json
48
+ {
49
+ "version": "X.Y.Z" // Replace with your version
50
+ }
51
+ ```
52
+
53
+ 3. **Update dependencies**
54
+
55
+ ```bash
56
+ bundle install
57
+ ```
58
+
59
+ Expected: Gemfile.lock updates with new version.
60
+
61
+ 4. **Update CHANGELOG**
62
+ - Add entry with the new version and date:
63
+
64
+ ```markdown
65
+ ## X.Y.Z (YYYY-MM-DD)
66
+
67
+ - [#PR_NUMBER](https://github.com/Shopify/shopify_app/pull/PR_NUMBER) Description of change
68
+ - List all changes since last release
69
+ ```
70
+
71
+ 5. **Create and push PR**
72
+
73
+ ```bash
74
+ git add -A
75
+ git commit -m "Packaging for release vX.Y.Z"
76
+ git push origin release-vX.Y.Z
77
+ ```
78
+
79
+ - Title PR: "Packaging for release X.Y.Z"
80
+ - Add release notes to PR description
81
+
82
+ ### Step 3: Tag and Publish
83
+
84
+ 1. **After PR is merged, update local main**
85
+
86
+ ```bash
87
+ git checkout main
88
+ git pull origin main
89
+ ```
90
+
91
+ Verify: `git log -1` shows your merge commit.
92
+
93
+ 2. **Create and push tag**
94
+
95
+ ```bash
96
+ git tag -f vX.Y.Z && git push origin vX.Y.Z
97
+ ```
98
+
99
+ Verify: Tag appears at https://github.com/Shopify/shopify_app/tags
100
+
101
+ 3. **Check Create Release workflow**
102
+
103
+ Monitor the GitHub Actions workflow to ensure it completes successfully.
104
+
105
+ 4. **Publish via Shipit**
106
+
107
+ Use Shipit to build and push the gem to RubyGems.
108
+
109
+ Note: If you see an error like 'You need to create the vX.Y.X tag first', clear git cache in Shipit settings.
110
+
111
+ 5. **Verify gem publication**
112
+
113
+ Check the gem on https://rubygems.org/gems/shopify_app
114
+
115
+ Expected: Shows your new version (may take 5-10 minutes).
data/docs/Upgrading.md CHANGED
@@ -8,6 +8,8 @@ This file documents important changes needed to upgrade your app's Shopify App v
8
8
 
9
9
  [Unreleased](#unreleased)
10
10
 
11
+ [Upgrading to `v23.0.0`](#upgrading-to-v2300)
12
+
11
13
  [Upgrading to `v22.2.0`](#upgrading-to-v2220)
12
14
 
13
15
  [Upgrading to `v22.0.0`](#upgrading-to-v2200)
@@ -44,6 +46,76 @@ If you do run into issues, we recommend looking at our [debugging tips.](https:/
44
46
 
45
47
  ## Unreleased
46
48
 
49
+ #### (v23.0.0) Minimum Ruby 3.2 and Rails 7.1 required, Jobs moved to app/jobs/
50
+
51
+ The minimum supported versions have been updated:
52
+ - **Ruby**: 3.1 → 3.2
53
+ - **Rails**: 5.2.1 → 7.1
54
+
55
+ Additionally, ActiveJob classes have been moved from `lib/shopify_app/jobs/` to `app/jobs/shopify_app/` to fix loading issues with modern Rails versions and follow Rails conventions.
56
+
57
+ ##### Why this change was made
58
+
59
+ Rails 7.1+'s improved autoloading behavior (via Zeitwerk) properly handles jobs in the `app/jobs/` directory, loading them lazily when needed rather than eagerly during initialization.
60
+
61
+ ##### Migration steps
62
+
63
+ **For most apps**: No changes needed. The jobs are internal to the gem and will be autoloaded correctly by Rails.
64
+
65
+ **If your app has custom ActiveJob serializers that reference these jobs**:
66
+ 1. Ensure you're on Rails 7.1+ before upgrading. [Rails 7.1 Upgrade Guide](https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-7-0-to-rails-7-1)
67
+ 2. Coordinate deployment timing with apps that have custom serializers
68
+ 3. Custom serializers will now register before jobs are loaded (correct timing)
69
+
70
+ **If your app directly requires or references job file paths** (rare):
71
+ ```ruby
72
+ # ❌ Old path (will break)
73
+ require 'shopify_app/jobs/webhooks_manager_job'
74
+
75
+ # ✅ No require needed - Rails autoloads from app/jobs/
76
+ # Jobs are available as ShopifyApp::WebhooksManagerJob
77
+ ```
78
+
79
+ ##### Additional dependency changes
80
+
81
+ - **sprockets-rails**: Now a required runtime dependency. Most Rails apps already include this, but if your app uses an alternative asset pipeline (e.g., Propshaft), you may need to add `sprockets-rails` to your Gemfile.
82
+
83
+ #### (v23.0.0) - ShopSessionStorageWithScopes and UserSessionStorageWithScopes are deprecated
84
+
85
+ `ShopSessionStorageWithScopes` and `UserSessionStorageWithScopes` are now marked as deprecated and will be removed in v24.0.0 in favor of `ShopSessionStorage` and `UserSessionStorage`, which handle all session attributes automatically (including `access_scopes`, `expires_at`, `refresh_token`, and `refresh_token_expires_at` for shops).
86
+
87
+ **Migration:**
88
+
89
+ 1. Update your Shop model to use `ShopSessionStorage`:
90
+ ```ruby
91
+ # Before
92
+ class Shop < ActiveRecord::Base
93
+ include ShopifyApp::ShopSessionStorageWithScopes
94
+ end
95
+
96
+ # After
97
+ class Shop < ActiveRecord::Base
98
+ include ShopifyApp::ShopSessionStorage
99
+ end
100
+ ```
101
+
102
+ 2. Update your User model to use `UserSessionStorage`:
103
+ ```ruby
104
+ # Before
105
+ class User < ActiveRecord::Base
106
+ include ShopifyApp::UserSessionStorageWithScopes
107
+ end
108
+
109
+ # After
110
+ class User < ActiveRecord::Base
111
+ include ShopifyApp::UserSessionStorage
112
+ end
113
+ ```
114
+
115
+ 3. **Optional:** You can now opt-in to using expiring offline access tokens with automatic refresh. See the [Sessions documentation](/docs/shopify_app/sessions.md#offline-access-tokens) for setup instructions.
116
+
117
+ **Note:** If you had custom `access_scopes=` or `access_scopes` methods in your models, these are no longer needed. The base concerns now handle these attributes automatically.
118
+
47
119
  #### (v23.0.0) - Deprecated methods in CallbackController
48
120
  The following methods from `ShopifyApp::CallbackController` have been deprecated in `v23.0.0`
49
121
  - `perform_after_authenticate_job`
@@ -1,10 +1,57 @@
1
1
  # Content Security Policy Header
2
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.
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 typically done by including the [`ShopifyApp::Authenticated`](https://github.com/Shopify/shopify_app/blob/ed41165ca9598d2c9d514487365192f22b5eb096/app/controllers/concerns/shopify_app/authenticated.rb) controller concern rather than directly including it.
4
4
 
5
- ## Included Domains
5
+ ## Frame Ancestors
6
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):
7
+ For actions that include the `ShopifyApp::FrameAncestors` controller concern, the following hosts are added to the `frame-ancestors` directive as [per the store requirements](https://shopify.dev/apps/store/security/iframe-protection#embedded-apps):
8
8
 
9
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
10
  2. "https://admin.shopify.com"
11
+
12
+ ## Strict Content Security Policy
13
+
14
+ If you enable a strict Content Security Policy in your application, you'll need to explicitly allow Shopify's App Bridge script. The gem provides a helper method to make this easy.
15
+
16
+ ### Without Strict CSP (Default)
17
+
18
+ By default, Shopify app templates have CSP **disabled** (commented out in `config/initializers/content_security_policy.rb`). In this configuration:
19
+ - Inline scripts work (including Vite HMR for development)
20
+ - App Bridge loads without any configuration
21
+ - No `script-src` directive is set
22
+
23
+ ### With Strict CSP
24
+
25
+ If you enable a strict CSP by uncommenting and configuring `content_security_policy.rb`, you must:
26
+
27
+ 1. **Handle inline scripts** - Strict CSP blocks inline scripts. You can:
28
+ - Use `unsafe-inline` in development (for Vite HMR)
29
+ - Use nonces or hashes in production
30
+ - Move inline scripts to external files
31
+
32
+ 2. **Allow App Bridge script** - Add the App Bridge script source using the provided helper:
33
+
34
+ ```ruby
35
+ # config/initializers/content_security_policy.rb
36
+ Rails.application.configure do
37
+ config.content_security_policy do |policy|
38
+ policy.default_src :self, :https
39
+
40
+ # For development: allow inline scripts for Vite HMR
41
+ if Rails.env.development?
42
+ policy.script_src :self, :unsafe_inline
43
+ else
44
+ policy.script_src :self
45
+ end
46
+
47
+ # Add Shopify App Bridge script source
48
+ ShopifyApp.add_csp_directives(policy)
49
+ end
50
+ end
51
+ ```
52
+
53
+ The `ShopifyApp.add_csp_directives(policy)` helper method:
54
+ - Adds `https://cdn.shopify.com/shopifycloud/app-bridge.js` to your `script-src` directive
55
+ - Preserves your existing `script-src` configuration
56
+ - Prevents duplicate URLs if called multiple times
57
+ - Is a no-op if you don't configure CSP
@@ -64,6 +64,26 @@ Implements Rails' [protect_from_forgery](https://api.rubyonrails.org/classes/Act
64
64
  #### EmbeddedApp
65
65
  If your ShopifyApp configuration has the `embedded_app` config set to true, [P3P header](https://www.w3.org/P3P/) and [content security policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) are handled for you.
66
66
 
67
+ ##### Content Security Policy (CSP) Directives:
68
+
69
+ The EmbeddedApp concern automatically configures the following CSP directives to ensure your embedded app works correctly within Shopify Admin:
70
+
71
+ 1. **frame-ancestors**: Allows the app to be embedded in iframes from:
72
+ - The current shop domain (e.g., `https://example.myshopify.com`)
73
+ - Shopify's unified admin domain (e.g., `https://admin.shopify.com`)
74
+
75
+ 2. **script-src**: Allows JavaScript execution from:
76
+ - `'self'` - Scripts from your app's own domain
77
+ - `https://cdn.shopify.com/shopifycloud/app-bridge.js` - Required for App Bridge functionality
78
+ - Any other script sources you explicitly add in your controller
79
+
80
+ These CSP settings ensure that:
81
+ - Your app can be properly embedded within Shopify Admin
82
+ - App Bridge can load and function correctly
83
+ - Your app maintains security while allowing necessary Shopify integrations
84
+
85
+ ##### Layout
86
+
67
87
  By default, the `EmbeddedApp` concern also sets the layout file to be `app/views/layouts/embedded_app.html.erb`.
68
88
 
69
89
  Sometimes one wants to run an embedded app in non-embedded mode. For example:
@@ -0,0 +1,52 @@
1
+ # Script Tags
2
+
3
+ ShopifyApp can manage your app's [Script Tags](https://shopify.dev/docs/admin-api/graphql/reference/online-store/scripttag) for you by setting which script tags you require in the initializer.
4
+ > [!NOTE]
5
+ > Script tags should only be used for vintage themes that do not support app blocks.
6
+
7
+ ## Configuration
8
+
9
+ ```ruby
10
+ ShopifyApp.configure do |config|
11
+ config.script_tags = [
12
+ # Basic script tag
13
+ {cache: true, src: 'https://example.com/fancy.js'},
14
+
15
+ # Script tag with template_types for app block detection
16
+ {
17
+ cache: true,
18
+ src: 'https://example.com/product-script.js',
19
+ template_types: ['product', 'collection']
20
+ }
21
+ ]
22
+ end
23
+ ```
24
+
25
+ ## Required Scopes
26
+ Both the `write_script_tags` and `read_themes` scopes are required.
27
+
28
+ For apps created with the Shopify CLI, set these scopes in your `shopify.app.toml` file:
29
+
30
+ ```toml
31
+ [access_scopes]
32
+ # Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
33
+ scopes = "write_products,write_script_tags,read_themes"
34
+ ```
35
+
36
+ For older apps, you can set the scopes in the initializer:
37
+
38
+ ```ruby
39
+ config.scope = 'write_products,write_script_tags,read_themes'
40
+ ```
41
+
42
+ ## How It Works
43
+
44
+ ### Script Tag Creation
45
+
46
+ Script tags are created in the same way as [Webhooks](/docs/shopify_app/webhooks.md), with a background job which will create the required scripttags.
47
+
48
+ ### App Block Detection
49
+
50
+ When you specify `template_types` for a script tag, ShopifyApp will check if the store's active theme supports app blocks for those template types. If any template type doesn't support app blocks, the script tags will be created as a fallback
51
+
52
+ This allows your app to automatically adapt to the store's theme capabilities, using app blocks when available and falling back to script tags when necessary.