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.
- checksums.yaml +4 -4
- data/.github/dependabot.yaml +6 -0
- data/.github/workflows/build.yml +5 -5
- data/.github/workflows/close-waiting-for-response-issues.yml +1 -1
- data/.github/workflows/release.yml +2 -2
- data/.github/workflows/remove-labels-on-activity.yml +2 -3
- data/.github/workflows/rubocop.yml +2 -2
- data/CHANGELOG.md +22 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +181 -122
- data/README.md +105 -25
- data/Rakefile +13 -0
- data/app/controllers/shopify_app/callback_controller.rb +2 -40
- data/app/controllers/shopify_app/extension_verification_controller.rb +1 -1
- data/app/jobs/shopify_app/script_tags_manager_job.rb +16 -0
- data/docs/Releasing.md +113 -19
- data/docs/Upgrading.md +72 -0
- data/docs/shopify_app/content-security-policy.md +50 -3
- data/docs/shopify_app/controller-concerns.md +20 -0
- data/docs/shopify_app/script-tags.md +52 -0
- data/docs/shopify_app/sessions.md +149 -22
- data/lib/generators/shopify_app/add_app_uninstalled_job/templates/app_uninstalled_job.rb.tt +2 -2
- data/lib/generators/shopify_app/add_privacy_jobs/templates/customers_data_request_job.rb.tt +2 -2
- data/lib/generators/shopify_app/add_privacy_jobs/templates/customers_redact_job.rb.tt +2 -2
- data/lib/generators/shopify_app/add_privacy_jobs/templates/shop_redact_job.rb.tt +2 -2
- data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +2 -2
- data/lib/generators/shopify_app/install/install_generator.rb +1 -1
- data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +18 -0
- data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_token_expiry_columns.erb +7 -0
- data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -1
- data/lib/generators/shopify_app/user_model/templates/user.rb +1 -1
- data/lib/shopify_app/auth/post_authenticate_tasks.rb +8 -0
- data/lib/shopify_app/auth/token_exchange.rb +7 -0
- data/lib/shopify_app/configuration.rb +5 -5
- data/lib/shopify_app/controller_concerns/login_protection.rb +12 -8
- data/lib/shopify_app/engine.rb +2 -5
- data/lib/shopify_app/errors.rb +2 -0
- data/lib/shopify_app/managers/script_tags_manager.rb +348 -0
- data/lib/shopify_app/session/shop_session_storage.rb +84 -2
- data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +6 -0
- data/lib/shopify_app/session/user_session_storage.rb +21 -2
- data/lib/shopify_app/session/user_session_storage_with_scopes.rb +6 -0
- data/lib/shopify_app/utils.rb +1 -1
- data/lib/shopify_app/version.rb +1 -1
- data/lib/shopify_app.rb +12 -7
- data/package.json +1 -1
- data/shopify_app.gemspec +9 -10
- data/translation.yml +1 -0
- data/yarn.lock +7 -9
- metadata +63 -46
- data/lib/shopify_app/middleware/jwt_middleware.rb +0 -48
- data/lib/shopify_app/session/jwt.rb +0 -73
- /data/{lib/shopify_app/jobs → app/jobs/shopify_app}/webhooks_manager_job.rb +0 -0
|
@@ -23,6 +23,7 @@ Sessions are used to make contextual API calls for either a shop (offline sessio
|
|
|
23
23
|
- [Access scopes](#access-scopes)
|
|
24
24
|
- [`ShopifyApp::ShopSessionStorageWithScopes`](#shopifyappshopsessionstoragewithscopes)
|
|
25
25
|
- [`ShopifyApp::UserSessionStorageWithScopes`](#shopifyappusersessionstoragewithscopes)
|
|
26
|
+
- [Migrating to Expiring Offline Access Tokens](#migrating-to-expiring-offline-access-tokens)
|
|
26
27
|
- [Migrating from shop-based to user-based token strategy](#migrating-from-shop-based-to-user-based-token-strategy)
|
|
27
28
|
- [Migrating from `ShopifyApi::Auth::SessionStorage` to `ShopifyApp::SessionStorage`](#migrating-from-shopifyapiauthsessionstorage-to-shopifyappsessionstorage)
|
|
28
29
|
|
|
@@ -129,11 +130,11 @@ These methods are already implemented as a part of the `User` and `Shop` models
|
|
|
129
130
|
Simply include these concerns if you want to use the implementation, and overwrite methods for custom implementation
|
|
130
131
|
|
|
131
132
|
- `Shop` storage
|
|
132
|
-
- [ShopSessionStorageWithScopes](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/session/shop_session_storage_with_scopes.rb)
|
|
133
|
+
- [ShopSessionStorageWithScopes](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/session/shop_session_storage_with_scopes.rb) (Deprecated in 23.0.0)
|
|
133
134
|
- [ShopSessionStorage](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/session/shop_session_storage.rb)
|
|
134
135
|
|
|
135
136
|
- `User` storage
|
|
136
|
-
- [UserSessionStorageWithScopes](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/session/user_session_storage_with_scopes.rb)
|
|
137
|
+
- [UserSessionStorageWithScopes](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/session/user_session_storage_with_scopes.rb) (Deprecated in 23.0.0)
|
|
137
138
|
- [UserSessionStorage](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/session/user_session_storage.rb)
|
|
138
139
|
|
|
139
140
|
### Loading Sessions
|
|
@@ -206,6 +207,18 @@ user.with_shopify_session do
|
|
|
206
207
|
end
|
|
207
208
|
```
|
|
208
209
|
|
|
210
|
+
**Automatic Token Refresh for Shop Sessions:**
|
|
211
|
+
|
|
212
|
+
When using `Shop` models with [expiring offline access tokens](#migrating-to-expiring-offline-access-tokens) configured, `with_shopify_session` will automatically refresh expired tokens before executing the block. This ensures your API calls always use valid credentials without manual intervention.
|
|
213
|
+
|
|
214
|
+
To disable automatic refresh, pass `auto_refresh: false`:
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
shop.with_shopify_session(auto_refresh: false) do
|
|
218
|
+
# Token will NOT be refreshed even if expired
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
209
222
|
#### Re-fetching an access token when API returns Unauthorized
|
|
210
223
|
|
|
211
224
|
When using `ShopifyApp::EnsureHasSession` and the `new_embedded_auth_strategy` configuration, any **unhandled** Unauthorized `ShopifyAPI::Errors::HttpResponseError` will cause the app to perform token exchange to fetch a new access token from Shopify and the action to be executed again. This will update and store the new access token to the current session instance.
|
|
@@ -300,39 +313,153 @@ class MyController < ApplicationController
|
|
|
300
313
|
end
|
|
301
314
|
```
|
|
302
315
|
|
|
303
|
-
##
|
|
304
|
-
|
|
316
|
+
## Expiry date
|
|
317
|
+
When the configuration flag `check_session_expiry_date` is set to true, the session expiry date will be checked to trigger a re-auth and get a fresh token when it is expired.
|
|
318
|
+
This requires the `ShopifyAPI::Auth::Session` `expires` attribute to be stored.
|
|
319
|
+
|
|
320
|
+
**Online access tokens (User sessions):**
|
|
321
|
+
- When the `User` model includes the `UserSessionStorage` concern, a DB migration can be generated with `rails generate shopify_app:user_model --skip` to add the `expires_at` attribute to the model
|
|
322
|
+
- Online access tokens cannot be refreshed, so when the token is expired, the user must go through the OAuth flow again to get a new token
|
|
323
|
+
|
|
324
|
+
**Offline access tokens (Shop sessions):**
|
|
325
|
+
- Offline access tokens can optionally be configured to expire and support automatic refresh. See [Migrating to Expiring Offline Access Tokens](#migrating-to-expiring-offline-access-tokens) for detailed setup instructions
|
|
326
|
+
|
|
327
|
+
## Migrating to Expiring Offline Access Tokens
|
|
328
|
+
|
|
329
|
+
You can opt-in to expiring offline access tokens for enhanced security. When enabled, Shopify will issue offline access tokens with an expiration date and a refresh token. `ShopSessionStorage` will then automatically refresh expired tokens when using `with_shopify_session`.
|
|
330
|
+
|
|
331
|
+
**1. Database Migration:**
|
|
332
|
+
|
|
333
|
+
Run the shop model generator (use `--skip` to avoid regenerating the Shop model if it already exists):
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
rails generate shopify_app:shop_model --skip
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The generator will prompt you to create a migration that adds the `expires_at`, `refresh_token`, and `refresh_token_expires_at` columns. Alternatively, you can create the migration manually:
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
class AddShopAccessTokenExpiryColumns < ActiveRecord::Migration[7.0]
|
|
343
|
+
def change
|
|
344
|
+
add_column :shops, :expires_at, :datetime
|
|
345
|
+
add_column :shops, :refresh_token, :string
|
|
346
|
+
add_column :shops, :refresh_token_expires_at, :datetime
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**2. Update Model Concern:**
|
|
352
|
+
|
|
353
|
+
If your Shop model is using the deprecated `ShopSessionStorageWithScopes` concern, update it to use `ShopSessionStorage`:
|
|
305
354
|
|
|
306
|
-
### `ShopifyApp::ShopSessionStorageWithScopes`
|
|
307
355
|
```ruby
|
|
356
|
+
# app/models/shop.rb
|
|
308
357
|
class Shop < ActiveRecord::Base
|
|
309
|
-
include ShopifyApp::ShopSessionStorageWithScopes
|
|
358
|
+
include ShopifyApp::ShopSessionStorage # Change from ShopSessionStorageWithScopes
|
|
359
|
+
end
|
|
360
|
+
```
|
|
310
361
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
362
|
+
`ShopSessionStorage` now automatically handles `access_scopes`, `expires_at`, `refresh_token`, and `refresh_token_expires_at` - no additional concerns needed.
|
|
363
|
+
|
|
364
|
+
**3. Configuration:**
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
# config/initializers/shopify_app.rb
|
|
368
|
+
ShopifyApp.configure do |config|
|
|
369
|
+
# ... other configuration
|
|
370
|
+
|
|
371
|
+
# Enable automatic reauthentication when session is expired
|
|
372
|
+
config.check_session_expiry_date = true
|
|
317
373
|
end
|
|
374
|
+
|
|
375
|
+
# For ShopifyAPI Context - enable requesting expiring offline tokens
|
|
376
|
+
ShopifyAPI::Context.setup(
|
|
377
|
+
# ... other configuration
|
|
378
|
+
expiring_offline_access_tokens: true, # Opt-in to start requesting expiring offline tokens
|
|
379
|
+
)
|
|
318
380
|
```
|
|
319
381
|
|
|
320
|
-
|
|
382
|
+
**4. Refreshing Expired Tokens:**
|
|
383
|
+
|
|
384
|
+
With the configuration enabled, expired tokens are automatically handled differently based on the flow:
|
|
385
|
+
|
|
386
|
+
**For user-facing requests (OAuth/Token Exchange flow):**
|
|
387
|
+
When `check_session_expiry_date` is enabled, expired sessions trigger automatic re-authentication through the OAuth flow. This happens transparently when using controller concerns like `EnsureHasSession`.
|
|
388
|
+
|
|
389
|
+
**For background jobs and non-user interactions:**
|
|
390
|
+
Tokens are automatically refreshed when using `with_shopify_session` from `ShopSessionStorage`:
|
|
391
|
+
|
|
321
392
|
```ruby
|
|
322
|
-
|
|
323
|
-
include ShopifyApp::UserSessionStorageWithScopes
|
|
393
|
+
shop = Shop.find_by(shopify_domain: "example.myshopify.com")
|
|
324
394
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
395
|
+
# Automatic refresh (default behavior)
|
|
396
|
+
shop.with_shopify_session do
|
|
397
|
+
# If the token is expired, it will be automatically refreshed before making API calls
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Disable automatic refresh if needed
|
|
401
|
+
shop.with_shopify_session(auto_refresh: false) do
|
|
402
|
+
# Token will NOT be refreshed even if expired
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Manual refresh
|
|
406
|
+
begin
|
|
407
|
+
shop.refresh_token_if_expired!
|
|
408
|
+
rescue ShopifyApp::RefreshTokenExpiredError
|
|
409
|
+
# Handle case where refresh token itself has expired
|
|
410
|
+
# App needs to go through OAuth flow again
|
|
411
|
+
end
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**Error Handling:**
|
|
415
|
+
- `ShopifyApp::RefreshTokenExpiredError` is raised when the refresh token itself is expired
|
|
416
|
+
- When this happens, the user must interact with the app to go through the OAuth flow again to get new tokens
|
|
417
|
+
- The refresh process uses database row-level locking to prevent race conditions from concurrent requests
|
|
418
|
+
|
|
419
|
+
**5. Migrating Existing Shop Installations:**
|
|
420
|
+
|
|
421
|
+
⚠️ **Important:** When you enable `offline_access_token_expires: true`, only **new shop installations** will automatically receive expiring tokens during the OAuth flow. Existing shop installations with non-expiring tokens will continue using their current tokens until manually migrated.
|
|
422
|
+
|
|
423
|
+
To migrate existing shops to expiring tokens, use the `ShopifyAPI::Auth::TokenExchange.migrate_to_expiring_token` method. Here's an example background job to migrate all existing shops:
|
|
424
|
+
|
|
425
|
+
```ruby
|
|
426
|
+
# app/jobs/migrate_shops_to_expiring_tokens_job.rb
|
|
427
|
+
class MigrateShopsToExpiringTokensJob < ActiveJob::Base
|
|
428
|
+
queue_as :default
|
|
429
|
+
|
|
430
|
+
def perform
|
|
431
|
+
# Find shops that haven't been migrated yet (no refresh_token or expires_at)
|
|
432
|
+
shops_to_migrate = Shop.where(expires_at: nil, refresh_token: nil, refresh_token_expires_at: nil)
|
|
433
|
+
|
|
434
|
+
shops_to_migrate.find_each do |shop|
|
|
435
|
+
begin
|
|
436
|
+
# Migrate to expiring token
|
|
437
|
+
new_session = ShopifyAPI::Auth::TokenExchange.migrate_to_expiring_token(
|
|
438
|
+
shop: shop.shopify_domain,
|
|
439
|
+
non_expiring_offline_token: shop.shopify_token
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
# Store the new session with expiring token and refresh token
|
|
443
|
+
Shop.store(new_session)
|
|
444
|
+
|
|
445
|
+
Rails.logger.info("Successfully migrated #{shop.shopify_domain} to expiring token")
|
|
446
|
+
rescue ShopifyAPI::Errors::HttpResponseError => e
|
|
447
|
+
# Handle migration errors (e.g., shop uninstalled, network issues)
|
|
448
|
+
Rails.logger.error("Failed to migrate #{shop.shopify_domain}: #{e.message}")
|
|
449
|
+
rescue => e
|
|
450
|
+
Rails.logger.error("Unexpected error migrating #{shop.shopify_domain}: #{e.message}")
|
|
451
|
+
end
|
|
452
|
+
end
|
|
330
453
|
end
|
|
331
454
|
end
|
|
332
455
|
```
|
|
333
456
|
|
|
334
|
-
|
|
335
|
-
|
|
457
|
+
**Migration notes:**
|
|
458
|
+
- This is a **one-time, irreversible operation** per shop
|
|
459
|
+
- The shop must have the app installed and have a valid access token
|
|
460
|
+
- After migration, the shop's offline token will have an expiration date and a refresh token
|
|
461
|
+
|
|
462
|
+
**Note:** If you choose not to enable expiring offline tokens, the `expires_at`, `refresh_token`, and `refresh_token_expires_at` columns will remain `NULL` and no automatic refresh will occur. Refresh tokens are only available for offline (shop) access tokens. Online (user) access tokens do not support refresh and must be re-authorized through OAuth when expired.
|
|
336
463
|
|
|
337
464
|
## Migrating from shop-based to user-based token strategy
|
|
338
465
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class AppUninstalledJob < ActiveJob::Base
|
|
2
|
-
|
|
2
|
+
include ShopifyAPI::Webhooks::WebhookHandler
|
|
3
3
|
|
|
4
4
|
class << self
|
|
5
|
-
def handle(topic:, shop:, body:)
|
|
5
|
+
def handle(topic:, shop:, body:, webhook_id:, api_version:)
|
|
6
6
|
perform_later(topic: topic, shop_domain: shop, webhook: body)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class CustomersDataRequestJob < ActiveJob::Base
|
|
2
|
-
|
|
2
|
+
include ShopifyAPI::Webhooks::WebhookHandler
|
|
3
3
|
|
|
4
4
|
class << self
|
|
5
|
-
def handle(topic:, shop:, body:)
|
|
5
|
+
def handle(topic:, shop:, body:, webhook_id:, api_version:)
|
|
6
6
|
perform_later(topic: topic, shop_domain: shop, webhook: body)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class CustomersRedactJob < ActiveJob::Base
|
|
2
|
-
|
|
2
|
+
include ShopifyAPI::Webhooks::WebhookHandler
|
|
3
3
|
|
|
4
4
|
class << self
|
|
5
|
-
def handle(topic:, shop:, body:)
|
|
5
|
+
def handle(topic:, shop:, body:, webhook_id:, api_version:)
|
|
6
6
|
perform_later(topic: topic, shop_domain: shop, webhook: body)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class ShopRedactJob < ActiveJob::Base
|
|
2
|
-
|
|
2
|
+
include ShopifyAPI::Webhooks::WebhookHandler
|
|
3
3
|
|
|
4
4
|
class << self
|
|
5
|
-
def handle(topic:, shop:, body:)
|
|
5
|
+
def handle(topic:, shop:, body:, webhook_id:, api_version:)
|
|
6
6
|
perform_later(topic: topic, shop_domain: shop, webhook: body)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class <%= @job_class_name %> < ActiveJob::Base
|
|
2
|
-
|
|
2
|
+
include ShopifyAPI::Webhooks::WebhookHandler
|
|
3
3
|
|
|
4
4
|
class << self
|
|
5
|
-
def handle(topic:, shop:, body:)
|
|
5
|
+
def handle(topic:, shop:, body:, webhook_id:, api_version:)
|
|
6
6
|
perform_later(topic: topic, shop_domain: shop, webhook: body)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -19,7 +19,7 @@ module ShopifyApp
|
|
|
19
19
|
def create_shopify_app_initializer
|
|
20
20
|
@application_name = format_array_argument(options["application_name"])
|
|
21
21
|
@scope = format_array_argument(options["scope"])
|
|
22
|
-
@api_version = options["api_version"] ||
|
|
22
|
+
@api_version = options["api_version"] || "2025-10"
|
|
23
23
|
|
|
24
24
|
template("shopify_app.rb", "config/initializers/shopify_app.rb")
|
|
25
25
|
end
|
|
@@ -40,6 +40,24 @@ module ShopifyApp
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
def create_shop_with_token_refresh_migration
|
|
44
|
+
token_refresh_prompt = <<~PROMPT
|
|
45
|
+
To support expiring offline access token with refresh, your Shop model needs to store \
|
|
46
|
+
token expiration dates and refresh tokens.
|
|
47
|
+
|
|
48
|
+
The following migration will add `expires_at`, `refresh_token`, and \
|
|
49
|
+
`refresh_token_expires_at` columns to the Shop model. \
|
|
50
|
+
Do you want to include this migration? [y/n]
|
|
51
|
+
PROMPT
|
|
52
|
+
|
|
53
|
+
if new_shopify_cli_app? || Rails.env.test? || yes?(token_refresh_prompt)
|
|
54
|
+
migration_template(
|
|
55
|
+
"db/migrate/add_shop_access_token_expiry_columns.erb",
|
|
56
|
+
"db/migrate/add_shop_access_token_expiry_columns.rb",
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
43
61
|
def update_shopify_app_initializer
|
|
44
62
|
gsub_file("config/initializers/shopify_app.rb", "ShopifyApp::InMemoryShopSessionStore", "Shop")
|
|
45
63
|
end
|
|
@@ -10,6 +10,7 @@ module ShopifyApp
|
|
|
10
10
|
session_for_shop = session.online? ? shop_session(session) : session
|
|
11
11
|
|
|
12
12
|
install_webhooks(session_for_shop)
|
|
13
|
+
install_scripttags(session_for_shop)
|
|
13
14
|
|
|
14
15
|
perform_after_authenticate_job(session)
|
|
15
16
|
end
|
|
@@ -27,6 +28,13 @@ module ShopifyApp
|
|
|
27
28
|
WebhooksManager.queue(session.shop, session.access_token)
|
|
28
29
|
end
|
|
29
30
|
|
|
31
|
+
def install_scripttags(session)
|
|
32
|
+
ShopifyApp::Logger.debug("PostAuthenticateTasks: Installing scripttags")
|
|
33
|
+
return unless ShopifyApp.configuration.has_script_tags?
|
|
34
|
+
|
|
35
|
+
ScriptTagsManager.queue(session.shop, session.access_token, ShopifyApp.configuration.script_tags)
|
|
36
|
+
end
|
|
37
|
+
|
|
30
38
|
def perform_after_authenticate_job(session)
|
|
31
39
|
ShopifyApp::Logger.debug("PostAuthenticateTasks: Performing after_authenticate_job")
|
|
32
40
|
config = ShopifyApp.configuration.after_authenticate_job
|
|
@@ -60,6 +60,13 @@ module ShopifyApp
|
|
|
60
60
|
rescue ActiveRecord::RecordNotUnique
|
|
61
61
|
Logger.debug("Session not stored due to concurrent token exchange calls")
|
|
62
62
|
session
|
|
63
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
64
|
+
if e.message.include?("has already been taken")
|
|
65
|
+
Logger.debug("Session not stored due to concurrent token exchange calls")
|
|
66
|
+
session
|
|
67
|
+
else
|
|
68
|
+
raise
|
|
69
|
+
end
|
|
63
70
|
rescue => error
|
|
64
71
|
Logger.error("An error occurred during the token exchange: [#{error.class}] #{error.message}")
|
|
65
72
|
raise
|
|
@@ -15,7 +15,7 @@ module ShopifyApp
|
|
|
15
15
|
attr_accessor :embedded_app
|
|
16
16
|
alias_method :embedded_app?, :embedded_app
|
|
17
17
|
attr_accessor :webhooks
|
|
18
|
-
attr_accessor :
|
|
18
|
+
attr_accessor :script_tags
|
|
19
19
|
attr_accessor :after_authenticate_job
|
|
20
20
|
attr_accessor :api_version
|
|
21
21
|
|
|
@@ -33,7 +33,7 @@ module ShopifyApp
|
|
|
33
33
|
attr_accessor :custom_post_authenticate_tasks
|
|
34
34
|
|
|
35
35
|
# customise ActiveJob queue names
|
|
36
|
-
attr_accessor :
|
|
36
|
+
attr_accessor :script_tags_manager_queue_name
|
|
37
37
|
attr_accessor :webhooks_manager_queue_name
|
|
38
38
|
|
|
39
39
|
# configure myshopify domain for local shopify development
|
|
@@ -58,7 +58,7 @@ module ShopifyApp
|
|
|
58
58
|
@root_url = "/"
|
|
59
59
|
@myshopify_domain = "myshopify.com"
|
|
60
60
|
@unified_admin_domain = "shopify.com"
|
|
61
|
-
@
|
|
61
|
+
@script_tags_manager_queue_name = Rails.application.config.active_job.queue_name
|
|
62
62
|
@webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
|
|
63
63
|
@disable_webpacker = ENV["SHOPIFY_APP_DISABLE_WEBPACKER"].present?
|
|
64
64
|
@scope = []
|
|
@@ -115,8 +115,8 @@ module ShopifyApp
|
|
|
115
115
|
webhooks.present?
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
-
def
|
|
119
|
-
|
|
118
|
+
def has_script_tags?
|
|
119
|
+
script_tags.present?
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def requires_billing?
|
|
@@ -85,13 +85,7 @@ module ShopifyApp
|
|
|
85
85
|
# Make sure the shop is set in the redirection URL
|
|
86
86
|
unless params[:shop]
|
|
87
87
|
ShopifyApp::Logger.debug("Setting current shop session")
|
|
88
|
-
params[:shop] =
|
|
89
|
-
current_shopify_session.shop
|
|
90
|
-
|
|
91
|
-
elsif shopify_id_token
|
|
92
|
-
jwt_payload = ShopifyAPI::Auth::JwtPayload.new(shopify_id_token)
|
|
93
|
-
jwt_payload.shop
|
|
94
|
-
end
|
|
88
|
+
params[:shop] = current_shopify_session&.shop || parse_shop_from_jwt
|
|
95
89
|
end
|
|
96
90
|
|
|
97
91
|
url ||= login_url_with_optional_shop
|
|
@@ -269,7 +263,7 @@ module ShopifyApp
|
|
|
269
263
|
return ShopifyAPI::Context.load_private_session if ShopifyAPI::Context.private?
|
|
270
264
|
|
|
271
265
|
session_id = ShopifyAPI::Utils::SessionUtils.current_session_id(shopify_id_token, cookies, is_online)
|
|
272
|
-
return
|
|
266
|
+
return unless session_id
|
|
273
267
|
|
|
274
268
|
ShopifyApp::SessionRepository.load_session(session_id)
|
|
275
269
|
end
|
|
@@ -279,5 +273,15 @@ module ShopifyApp
|
|
|
279
273
|
request.media_type == "text/javascript" ||
|
|
280
274
|
request.media_type == "application/javascript"
|
|
281
275
|
end
|
|
276
|
+
|
|
277
|
+
def parse_shop_from_jwt
|
|
278
|
+
return nil unless shopify_id_token
|
|
279
|
+
|
|
280
|
+
jwt_payload = ShopifyAPI::Auth::JwtPayload.new(shopify_id_token)
|
|
281
|
+
jwt_payload.shop
|
|
282
|
+
rescue ShopifyAPI::Errors::InvalidJwtTokenError
|
|
283
|
+
ShopifyApp::Logger.warn("Invalid JWT token for current Shopify session")
|
|
284
|
+
nil
|
|
285
|
+
end
|
|
282
286
|
end
|
|
283
287
|
end
|
data/lib/shopify_app/engine.rb
CHANGED
|
@@ -5,7 +5,7 @@ module ShopifyApp
|
|
|
5
5
|
private
|
|
6
6
|
|
|
7
7
|
def args_info(job)
|
|
8
|
-
log_disabled_classes = ["ShopifyApp::WebhooksManagerJob"]
|
|
8
|
+
log_disabled_classes = ["ShopifyApp::WebhooksManagerJob", "ShopifyApp::ScriptTagsManagerJob"]
|
|
9
9
|
return "" if log_disabled_classes.include?(job.class.name)
|
|
10
10
|
|
|
11
11
|
super
|
|
@@ -16,10 +16,6 @@ module ShopifyApp
|
|
|
16
16
|
engine_name "shopify_app"
|
|
17
17
|
isolate_namespace ShopifyApp
|
|
18
18
|
|
|
19
|
-
initializer "shopify_app.middleware" do |app|
|
|
20
|
-
app.config.middleware.insert_after(::Rack::Runtime, ShopifyApp::JWTMiddleware)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
19
|
initializer "shopify_app.assets.precompile" do |app|
|
|
24
20
|
app.config.assets.precompile += [
|
|
25
21
|
"shopify_app/redirect.js",
|
|
@@ -30,6 +26,7 @@ module ShopifyApp
|
|
|
30
26
|
ActiveSupport.on_load(:active_job) do
|
|
31
27
|
if ActiveJob::Base.respond_to?(:log_arguments?)
|
|
32
28
|
WebhooksManagerJob.log_arguments = false
|
|
29
|
+
ScriptTagsManagerJob.log_arguments = false
|
|
33
30
|
elsif ActiveJob::Logging::LogSubscriber.private_method_defined?(:args_info)
|
|
34
31
|
ActiveJob::Logging::LogSubscriber.prepend(RedactJobParams)
|
|
35
32
|
end
|