shopify_app 18.0.2 → 19.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +4 -5
  3. data/.gitignore +1 -0
  4. data/.nvmrc +1 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +52 -2
  7. data/CONTRIBUTING.md +6 -1
  8. data/Gemfile +3 -2
  9. data/Gemfile.lock +144 -164
  10. data/README.md +1 -0
  11. data/Rakefile +4 -3
  12. data/app/assets/javascripts/shopify_app/app_bridge_2.0.12.js +10 -0
  13. data/app/assets/javascripts/shopify_app/app_bridge_redirect.js +22 -0
  14. data/app/assets/javascripts/shopify_app/redirect.js +9 -11
  15. data/app/assets/javascripts/shopify_app/storage_access.js +4 -10
  16. data/app/controllers/concerns/shopify_app/authenticated.rb +3 -0
  17. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +16 -3
  18. data/app/controllers/concerns/shopify_app/require_known_shop.rb +1 -0
  19. data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +1 -1
  20. data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
  21. data/app/controllers/shopify_app/callback_controller.rb +49 -134
  22. data/app/controllers/shopify_app/sessions_controller.rb +26 -131
  23. data/app/controllers/shopify_app/webhooks_controller.rb +5 -24
  24. data/app/views/shopify_app/sessions/enable_cookies.html.erb +1 -1
  25. data/app/views/shopify_app/sessions/request_storage_access.html.erb +11 -11
  26. data/app/views/shopify_app/sessions/top_level_interaction.html.erb +1 -1
  27. data/app/views/shopify_app/shared/redirect.html.erb +2 -2
  28. data/config/locales/zh-CN.yml +1 -1
  29. data/config/routes.rb +20 -12
  30. data/docs/Troubleshooting.md +0 -3
  31. data/docs/Upgrading.md +116 -14
  32. data/docs/shopify_app/engine.md +2 -2
  33. data/docs/shopify_app/handling-access-scopes-changes.md +11 -1
  34. data/docs/shopify_app/script-tags.md +1 -1
  35. data/docs/shopify_app/webhooks.md +3 -3
  36. data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +10 -9
  37. data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -0
  38. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +4 -3
  39. data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +13 -12
  40. data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +9 -1
  41. data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +7 -6
  42. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +2 -1
  43. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +1 -1
  44. data/lib/generators/shopify_app/authenticated_controller/authenticated_controller_generator.rb +3 -3
  45. data/lib/generators/shopify_app/controllers/controllers_generator.rb +4 -3
  46. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +11 -15
  47. data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +2 -2
  48. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +7 -3
  49. data/lib/generators/shopify_app/install/install_generator.rb +27 -72
  50. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +3 -1
  51. data/lib/generators/shopify_app/install/templates/session_store.rb +2 -1
  52. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +33 -5
  53. data/lib/generators/shopify_app/install/templates/shopify_app_importmap.js +13 -0
  54. data/lib/generators/shopify_app/products_controller/products_controller_generator.rb +3 -3
  55. data/lib/generators/shopify_app/products_controller/templates/products_controller.rb +1 -1
  56. data/lib/generators/shopify_app/rotate_shopify_token_job/rotate_shopify_token_job_generator.rb +4 -4
  57. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token.rake +1 -0
  58. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb +1 -1
  59. data/lib/generators/shopify_app/routes/routes_generator.rb +6 -5
  60. data/lib/generators/shopify_app/routes/templates/routes.rb +5 -5
  61. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +11 -10
  62. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -0
  63. data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
  64. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -0
  65. data/lib/generators/shopify_app/user_model/user_model_generator.rb +11 -10
  66. data/lib/generators/shopify_app/views/views_generator.rb +4 -3
  67. data/lib/shopify_app/access_scopes/shop_strategy.rb +2 -2
  68. data/lib/shopify_app/access_scopes/user_strategy.rb +4 -4
  69. data/lib/shopify_app/configuration.rb +33 -14
  70. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +4 -3
  71. data/lib/shopify_app/controller_concerns/csrf_protection.rb +2 -1
  72. data/lib/shopify_app/controller_concerns/embedded_app.rb +4 -3
  73. data/lib/shopify_app/controller_concerns/ensure_billing.rb +254 -0
  74. data/lib/shopify_app/controller_concerns/itp.rb +3 -3
  75. data/lib/shopify_app/controller_concerns/localization.rb +1 -0
  76. data/lib/shopify_app/controller_concerns/login_protection.rb +82 -68
  77. data/lib/shopify_app/controller_concerns/payload_verification.rb +3 -2
  78. data/lib/shopify_app/controller_concerns/webhook_verification.rb +2 -1
  79. data/lib/shopify_app/engine.rb +7 -15
  80. data/lib/shopify_app/jobs/scripttags_manager_job.rb +2 -2
  81. data/lib/shopify_app/jobs/webhooks_manager_job.rb +4 -5
  82. data/lib/shopify_app/managers/scripttags_manager.rb +11 -4
  83. data/lib/shopify_app/managers/webhooks_manager.rb +42 -44
  84. data/lib/shopify_app/middleware/jwt_middleware.rb +5 -3
  85. data/lib/shopify_app/session/in_memory_session_store.rb +1 -0
  86. data/lib/shopify_app/session/in_memory_shop_session_store.rb +2 -1
  87. data/lib/shopify_app/session/in_memory_user_session_store.rb +1 -0
  88. data/lib/shopify_app/session/jwt.rb +12 -7
  89. data/lib/shopify_app/session/null_user_session_store.rb +2 -1
  90. data/lib/shopify_app/session/session_repository.rb +37 -0
  91. data/lib/shopify_app/session/session_storage.rb +4 -6
  92. data/lib/shopify_app/session/shop_session_storage.rb +6 -6
  93. data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +7 -8
  94. data/lib/shopify_app/session/user_session_storage.rb +19 -6
  95. data/lib/shopify_app/session/user_session_storage_with_scopes.rb +22 -9
  96. data/lib/shopify_app/test_helpers/all.rb +2 -1
  97. data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +4 -3
  98. data/lib/shopify_app/utils.rb +4 -10
  99. data/lib/shopify_app/version.rb +2 -1
  100. data/lib/shopify_app.rb +44 -40
  101. data/package.json +1 -1
  102. data/shopify_app.gemspec +22 -21
  103. data/translation.yml +1 -1
  104. data/yarn.lock +103 -88
  105. metadata +51 -60
  106. data/config/locales/hi.yml +0 -23
  107. data/config/locales/ms.yml +0 -22
  108. data/lib/generators/shopify_app/install/templates/omniauth.rb +0 -4
  109. data/lib/generators/shopify_app/install/templates/shopify_provider.rb.tt +0 -8
  110. data/lib/generators/shopify_app/install/templates/user_agent.rb +0 -6
  111. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +0 -34
  112. data/lib/shopify_app/omniauth/omniauth_configuration.rb +0 -64
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module PayloadVerification
4
5
  extend ActiveSupport::Concern
@@ -6,14 +7,14 @@ module ShopifyApp
6
7
  private
7
8
 
8
9
  def shopify_hmac
9
- request.headers['HTTP_X_SHOPIFY_HMAC_SHA256']
10
+ request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"]
10
11
  end
11
12
 
12
13
  def hmac_valid?(data)
13
14
  secrets = [ShopifyApp.configuration.secret, ShopifyApp.configuration.old_secret].reject(&:blank?)
14
15
 
15
16
  secrets.any? do |secret|
16
- digest = OpenSSL::Digest.new('sha256')
17
+ digest = OpenSSL::Digest.new("sha256")
17
18
  ActiveSupport::SecurityUtils.secure_compare(
18
19
  shopify_hmac,
19
20
  Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, data))
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module WebhookVerification
4
5
  extend ActiveSupport::Concern
@@ -17,7 +18,7 @@ module ShopifyApp
17
18
  end
18
19
 
19
20
  def shop_domain
20
- request.headers['HTTP_X_SHOPIFY_SHOP_DOMAIN']
21
+ request.headers["HTTP_X_SHOPIFY_SHOP_DOMAIN"]
21
22
  end
22
23
  end
23
24
  end
@@ -1,36 +1,28 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module RedactJobParams
4
5
  private
5
6
 
6
7
  def args_info(job)
7
- log_disabled_classes = %w(ShopifyApp::ScripttagsManagerJob ShopifyApp::WebhooksManagerJob)
8
+ log_disabled_classes = ["ShopifyApp::ScripttagsManagerJob", "ShopifyApp::WebhooksManagerJob"]
8
9
  return "" if log_disabled_classes.include?(job.class.name)
9
10
  super
10
11
  end
11
12
  end
12
13
 
13
14
  class Engine < Rails::Engine
14
- engine_name 'shopify_app'
15
+ engine_name "shopify_app"
15
16
  isolate_namespace ShopifyApp
16
17
 
17
18
  initializer "shopify_app.assets.precompile" do |app|
18
- app.config.assets.precompile += %w[
19
- shopify_app/redirect.js
20
- shopify_app/post_redirect.js
21
- shopify_app/top_level.js
22
- shopify_app/enable_cookies.js
23
- shopify_app/request_storage_access.js
24
- storage_access.svg
25
- ]
19
+ app.config.assets.precompile += ["shopify_app/redirect.js", "shopify_app/post_redirect.js",
20
+ "shopify_app/top_level.js", "shopify_app/enable_cookies.js",
21
+ "shopify_app/request_storage_access.js", "storage_access.svg",]
26
22
  end
27
23
 
28
24
  initializer "shopify_app.middleware" do |app|
29
- app.config.middleware.insert_after(::Rack::Runtime, ShopifyApp::SameSiteCookieMiddleware)
30
-
31
- if ShopifyApp.configuration.allow_jwt_authentication
32
- app.config.middleware.insert_after(ShopifyApp::SameSiteCookieMiddleware, ShopifyApp::JWTMiddleware)
33
- end
25
+ app.config.middleware.insert_after(::Rack::Runtime, ShopifyApp::JWTMiddleware)
34
26
  end
35
27
 
36
28
  initializer "shopify_app.redact_job_params" do
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class ScripttagsManagerJob < ActiveJob::Base
4
5
  queue_as do
@@ -6,8 +7,7 @@ module ShopifyApp
6
7
  end
7
8
 
8
9
  def perform(shop_domain:, shop_token:, scripttags:)
9
- api_version = ShopifyApp.configuration.api_version
10
- ShopifyAPI::Session.temp(domain: shop_domain, token: shop_token, api_version: api_version) do
10
+ ShopifyAPI::Auth::Session.temp(shop: shop_domain, access_token: shop_token) do
11
11
  manager = ScripttagsManager.new(scripttags, shop_domain)
12
12
  manager.create_scripttags
13
13
  end
@@ -1,15 +1,14 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class WebhooksManagerJob < ActiveJob::Base
4
5
  queue_as do
5
6
  ShopifyApp.configuration.webhooks_manager_queue_name
6
7
  end
7
8
 
8
- def perform(shop_domain:, shop_token:, webhooks:)
9
- api_version = ShopifyApp.configuration.api_version
10
- ShopifyAPI::Session.temp(domain: shop_domain, token: shop_token, api_version: api_version) do
11
- manager = WebhooksManager.new(webhooks)
12
- manager.create_webhooks
9
+ def perform(shop_domain:, shop_token:)
10
+ ShopifyAPI::Auth::Session.temp(shop: shop_domain, access_token: shop_token) do |session|
11
+ WebhooksManager.create_webhooks(session: session)
13
12
  end
14
13
  end
15
14
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class ScripttagsManager
4
5
  class CreationFailed < StandardError; end
@@ -44,7 +45,7 @@ module ShopifyApp
44
45
  def destroy_scripttags
45
46
  scripttags = expanded_scripttags
46
47
  ShopifyAPI::ScriptTag.all.each do |tag|
47
- ShopifyAPI::ScriptTag.delete(tag.id) if required_scripttag?(scripttags, tag)
48
+ tag.delete if required_scripttag?(scripttags, tag)
48
49
  end
49
50
 
50
51
  @current_scripttags = nil
@@ -61,9 +62,15 @@ module ShopifyApp
61
62
  end
62
63
 
63
64
  def create_scripttag(attributes)
64
- attributes.reverse_merge!(format: 'json')
65
- scripttag = ShopifyAPI::ScriptTag.create(attributes)
66
- raise CreationFailed, scripttag.errors.full_messages.to_sentence unless scripttag.persisted?
65
+ scripttag = ShopifyAPI::ScriptTag.new
66
+ attributes.each { |key, value| scripttag.public_send("#{key}=", value) }
67
+
68
+ begin
69
+ scripttag.save!
70
+ rescue ShopifyAPI::Errors::HttpResponseError => e
71
+ raise CreationFailed, e.message
72
+ end
73
+
67
74
  scripttag
68
75
  end
69
76
 
@@ -1,62 +1,60 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class WebhooksManager
4
5
  class CreationFailed < StandardError; end
5
6
 
6
- def self.queue(shop_domain, shop_token, webhooks)
7
- ShopifyApp::WebhooksManagerJob.perform_later(
8
- shop_domain: shop_domain,
9
- shop_token: shop_token,
10
- webhooks: webhooks
11
- )
12
- end
13
-
14
- attr_reader :required_webhooks
15
-
16
- def initialize(webhooks)
17
- @required_webhooks = webhooks
18
- end
19
-
20
- def recreate_webhooks!
21
- destroy_webhooks
22
- create_webhooks
23
- end
24
-
25
- def create_webhooks
26
- return unless required_webhooks.present?
7
+ class << self
8
+ def queue(shop_domain, shop_token)
9
+ ShopifyApp::WebhooksManagerJob.perform_later(
10
+ shop_domain: shop_domain,
11
+ shop_token: shop_token
12
+ )
13
+ end
27
14
 
28
- required_webhooks.each do |webhook|
29
- create_webhook(webhook) unless webhook_exists?(webhook[:topic])
15
+ def create_webhooks(session:)
16
+ return unless ShopifyApp.configuration.has_webhooks?
17
+ ShopifyAPI::Webhooks::Registry.register_all(session: session)
30
18
  end
31
- end
32
19
 
33
- def destroy_webhooks
34
- ShopifyAPI::Webhook.all.to_a.each do |webhook|
35
- ShopifyAPI::Webhook.delete(webhook.id) if required_webhook?(webhook)
20
+ def recreate_webhooks!(session:)
21
+ destroy_webhooks
22
+ return unless ShopifyApp.configuration.has_webhooks?
23
+ add_registrations
24
+ ShopifyAPI::Webhooks::Registry.register_all(session: session)
36
25
  end
37
26
 
38
- @current_webhooks = nil
39
- end
27
+ def destroy_webhooks
28
+ return unless ShopifyApp.configuration.has_webhooks?
40
29
 
41
- private
30
+ ShopifyApp.configuration.webhooks.each do |attributes|
31
+ ShopifyAPI::Webhooks::Registry.unregister(topic: attributes[:topic])
32
+ end
33
+ end
42
34
 
43
- def required_webhook?(webhook)
44
- required_webhooks.map { |w| w[:address] }.include?(webhook.address)
45
- end
35
+ def add_registrations
36
+ return unless ShopifyApp.configuration.has_webhooks?
37
+
38
+ ShopifyApp.configuration.webhooks.each do |attributes|
39
+ ShopifyAPI::Webhooks::Registry.add_registration(
40
+ topic: attributes[:topic],
41
+ delivery_method: attributes[:delivery_method] || :http,
42
+ path: attributes[:address],
43
+ handler: webhook_job_klass(attributes[:topic]),
44
+ fields: attributes[:fields]
45
+ )
46
+ end
47
+ end
46
48
 
47
- def create_webhook(attributes)
48
- attributes.reverse_merge!(format: 'json')
49
- webhook = ShopifyAPI::Webhook.create(attributes)
50
- raise CreationFailed, webhook.errors.full_messages.to_sentence unless webhook.persisted?
51
- webhook
52
- end
49
+ private
53
50
 
54
- def webhook_exists?(topic)
55
- current_webhooks[topic]
56
- end
51
+ def webhook_job_klass(topic)
52
+ webhook_job_klass_name(topic).safe_constantize || raise(ShopifyApp::MissingWebhookJobError)
53
+ end
57
54
 
58
- def current_webhooks
59
- @current_webhooks ||= ShopifyAPI::Webhook.all.to_a.index_by(&:topic)
55
+ def webhook_job_klass_name(topic)
56
+ [ShopifyApp.configuration.webhook_jobs_namespace, "#{topic.gsub("/", "_")}_job"].compact.join("/").classify
57
+ end
60
58
  end
61
59
  end
62
60
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class JWTMiddleware
4
5
  TOKEN_REGEX = /^Bearer\s+(.*?)$/
@@ -24,7 +25,7 @@ module ShopifyApp
24
25
  end
25
26
 
26
27
  def authorization_header(env)
27
- env['HTTP_AUTHORIZATION']
28
+ env["HTTP_AUTHORIZATION"]
28
29
  end
29
30
 
30
31
  def extract_token(env)
@@ -35,8 +36,9 @@ module ShopifyApp
35
36
  def set_env_variables(token, env)
36
37
  jwt = ShopifyApp::JWT.new(token)
37
38
 
38
- env['jwt.shopify_domain'] = jwt.shopify_domain
39
- env['jwt.shopify_user_id'] = jwt.shopify_user_id
39
+ env["jwt.shopify_domain"] = jwt.shopify_domain
40
+ env["jwt.shopify_user_id"] = jwt.shopify_user_id
41
+ env["jwt.expire_at"] = jwt.expire_at
40
42
  end
41
43
  end
42
44
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  # rubocop:disable Style/ClassVars
4
5
  # Class var repo is needed here in order to share data between the 2 child classes.
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class InMemoryShopSessionStore < InMemorySessionStore
4
5
  class << self
5
6
  def store(session, *args)
6
7
  id = super
7
- repo[session.domain] = session
8
+ repo[session.shop] = session
8
9
  id
9
10
  end
10
11
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class InMemoryUserSessionStore < InMemorySessionStore
4
5
  class << self
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class JWT
4
5
  class InvalidDestinationError < StandardError; end
@@ -23,11 +24,15 @@ module ShopifyApp
23
24
  end
24
25
 
25
26
  def shopify_domain
26
- @payload && ShopifyApp::Utils.sanitize_shop_domain(@payload['dest'])
27
+ @payload && ShopifyApp::Utils.sanitize_shop_domain(@payload["dest"])
27
28
  end
28
29
 
29
30
  def shopify_user_id
30
- @payload['sub'].to_i if @payload && @payload['sub']
31
+ @payload["sub"].to_i if @payload && @payload["sub"]
32
+ end
33
+
34
+ def expire_at
35
+ @payload["exp"].to_i if @payload && @payload["exp"]
31
36
  end
32
37
 
33
38
  private
@@ -41,19 +46,19 @@ module ShopifyApp
41
46
  end
42
47
 
43
48
  def parse_token_data(secret, old_secret)
44
- ::JWT.decode(@token, secret, true, { algorithm: 'HS256' })
49
+ ::JWT.decode(@token, secret, true, { algorithm: "HS256" })
45
50
  rescue ::JWT::VerificationError
46
51
  raise unless old_secret
47
52
 
48
- ::JWT.decode(@token, old_secret, true, { algorithm: 'HS256' })
53
+ ::JWT.decode(@token, old_secret, true, { algorithm: "HS256" })
49
54
  end
50
55
 
51
56
  def validate_payload(payload)
52
- dest_host = ShopifyApp::Utils.sanitize_shop_domain(payload['dest'])
53
- iss_host = ShopifyApp::Utils.sanitize_shop_domain(payload['iss'])
57
+ dest_host = ShopifyApp::Utils.sanitize_shop_domain(payload["dest"])
58
+ iss_host = ShopifyApp::Utils.sanitize_shop_domain(payload["iss"])
54
59
  api_key = ShopifyApp.configuration.api_key
55
60
 
56
- raise InvalidAudienceError, "'aud' claim does not match api_key" unless payload['aud'] == api_key
61
+ raise InvalidAudienceError, "'aud' claim does not match api_key" unless payload["aud"] == api_key
57
62
  raise InvalidDestinationError, "'dest' claim host not a valid shopify host" unless dest_host
58
63
  raise MismatchedHostsError, "'dest' claim host does not match 'iss' claim host" unless dest_host == iss_host
59
64
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class NullUserSessionStore
4
5
  class << self
@@ -7,7 +8,7 @@ module ShopifyApp
7
8
  end
8
9
 
9
10
  def store(_, _)
10
- raise SessionRepository::ConfigurationError, 'user_storage is not configured'
11
+ raise SessionRepository::ConfigurationError, "user_storage is not configured"
11
12
  end
12
13
 
13
14
  def retrieve_by_shopify_user_id(_)
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class SessionRepository
5
+ extend ShopifyAPI::Auth::SessionStorage
6
+
4
7
  class ConfigurationError < StandardError; end
5
8
 
6
9
  class << self
@@ -40,6 +43,40 @@ module ShopifyApp
40
43
  load_user_storage
41
44
  end
42
45
 
46
+ # ShopifyAPI::Auth::SessionStorage override
47
+ def store_session(session)
48
+ if session.online?
49
+ user_storage.store(session, session.associated_user)
50
+ else
51
+ shop_storage.store(session)
52
+ end
53
+ end
54
+
55
+ # ShopifyAPI::Auth::SessionStorage override
56
+ def load_session(id)
57
+ match = id.match(/^offline_(.*)/)
58
+ if match
59
+ retrieve_shop_session_by_shopify_domain(match[1])
60
+ else
61
+ retrieve_user_session_by_shopify_user_id(id.split("_").last)
62
+ end
63
+ end
64
+
65
+ # ShopifyAPI::Auth::SessionStorage override
66
+ def delete_session(id)
67
+ match = id.match(/^offline_(.*)/)
68
+
69
+ record = if match
70
+ Shop.find_by(shopify_domain: match[1])
71
+ else
72
+ User.find_by(shopify_user_id: id.split("_").last)
73
+ end
74
+
75
+ record.destroy
76
+
77
+ true
78
+ end
79
+
43
80
  private
44
81
 
45
82
  def load_shop_storage
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module SessionStorage
4
5
  extend ActiveSupport::Concern
@@ -9,12 +10,9 @@ module ShopifyApp
9
10
  end
10
11
 
11
12
  def with_shopify_session(&block)
12
- ShopifyAPI::Session.temp(
13
- domain: shopify_domain,
14
- token: shopify_token,
15
- api_version: api_version,
16
- &block
17
- )
13
+ ShopifyAPI::Auth::Session.temp(shop: shopify_domain, access_token: shopify_token) do
14
+ yield block
15
+ end
18
16
  end
19
17
  end
20
18
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module ShopSessionStorage
4
5
  extend ActiveSupport::Concern
@@ -10,8 +11,8 @@ module ShopifyApp
10
11
 
11
12
  class_methods do
12
13
  def store(auth_session, *_args)
13
- shop = find_or_initialize_by(shopify_domain: auth_session.domain)
14
- shop.shopify_token = auth_session.token
14
+ shop = find_or_initialize_by(shopify_domain: auth_session.shop)
15
+ shop.shopify_token = auth_session.access_token
15
16
  shop.save!
16
17
  shop.id
17
18
  end
@@ -31,10 +32,9 @@ module ShopifyApp
31
32
  def construct_session(shop)
32
33
  return unless shop
33
34
 
34
- ShopifyAPI::Session.new(
35
- domain: shop.shopify_domain,
36
- token: shop.shopify_token,
37
- api_version: shop.api_version,
35
+ ShopifyAPI::Auth::Session.new(
36
+ shop: shop.shopify_domain,
37
+ access_token: shop.shopify_token
38
38
  )
39
39
  end
40
40
  end
@@ -11,9 +11,9 @@ module ShopifyApp
11
11
 
12
12
  class_methods do
13
13
  def store(auth_session, *_args)
14
- shop = find_or_initialize_by(shopify_domain: auth_session.domain)
15
- shop.shopify_token = auth_session.token
16
- shop.access_scopes = auth_session.access_scopes
14
+ shop = find_or_initialize_by(shopify_domain: auth_session.shop)
15
+ shop.shopify_token = auth_session.access_token
16
+ shop.access_scopes = auth_session.scope.to_s
17
17
 
18
18
  shop.save!
19
19
  shop.id
@@ -34,11 +34,10 @@ module ShopifyApp
34
34
  def construct_session(shop)
35
35
  return unless shop
36
36
 
37
- ShopifyAPI::Session.new(
38
- domain: shop.shopify_domain,
39
- token: shop.shopify_token,
40
- api_version: shop.api_version,
41
- access_scopes: shop.access_scopes
37
+ ShopifyAPI::Auth::Session.new(
38
+ shop: shop.shopify_domain,
39
+ access_token: shop.shopify_token,
40
+ scope: shop.access_scopes
42
41
  )
43
42
  end
44
43
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module UserSessionStorage
4
5
  extend ActiveSupport::Concern
@@ -11,8 +12,8 @@ module ShopifyApp
11
12
  class_methods do
12
13
  def store(auth_session, user)
13
14
  user = find_or_initialize_by(shopify_user_id: user[:id])
14
- user.shopify_token = auth_session.token
15
- user.shopify_domain = auth_session.domain
15
+ user.shopify_token = auth_session.access_token
16
+ user.shopify_domain = auth_session.shop
16
17
  user.save!
17
18
  user.id
18
19
  end
@@ -31,10 +32,22 @@ module ShopifyApp
31
32
 
32
33
  def construct_session(user)
33
34
  return unless user
34
- ShopifyAPI::Session.new(
35
- domain: user.shopify_domain,
36
- token: user.shopify_token,
37
- api_version: user.api_version,
35
+
36
+ associated_user = ShopifyAPI::Auth::AssociatedUser.new(
37
+ id: user.shopify_user_id,
38
+ first_name: "",
39
+ last_name: "",
40
+ email: "",
41
+ email_verified: false,
42
+ account_owner: false,
43
+ locale: "",
44
+ collaborator: false
45
+ )
46
+
47
+ ShopifyAPI::Auth::Session.new(
48
+ shop: user.shopify_domain,
49
+ access_token: user.shopify_token,
50
+ associated_user: associated_user
38
51
  )
39
52
  end
40
53
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module UserSessionStorageWithScopes
4
5
  extend ActiveSupport::Concern
@@ -10,10 +11,10 @@ module ShopifyApp
10
11
 
11
12
  class_methods do
12
13
  def store(auth_session, user)
13
- user = find_or_initialize_by(shopify_user_id: user[:id])
14
- user.shopify_token = auth_session.token
15
- user.shopify_domain = auth_session.domain
16
- user.access_scopes = auth_session.access_scopes
14
+ user = find_or_initialize_by(shopify_user_id: user.id)
15
+ user.shopify_token = auth_session.access_token
16
+ user.shopify_domain = auth_session.shop
17
+ user.access_scopes = auth_session.scope.to_s
17
18
 
18
19
  user.save!
19
20
  user.id
@@ -34,11 +35,23 @@ module ShopifyApp
34
35
  def construct_session(user)
35
36
  return unless user
36
37
 
37
- ShopifyAPI::Session.new(
38
- domain: user.shopify_domain,
39
- token: user.shopify_token,
40
- api_version: user.api_version,
41
- access_scopes: user.access_scopes
38
+ associated_user = ShopifyAPI::Auth::AssociatedUser.new(
39
+ id: user.shopify_user_id,
40
+ first_name: "",
41
+ last_name: "",
42
+ email: "",
43
+ email_verified: false,
44
+ account_owner: false,
45
+ locale: "",
46
+ collaborator: false
47
+ )
48
+
49
+ ShopifyAPI::Auth::Session.new(
50
+ shop: user.shopify_domain,
51
+ access_token: user.shopify_token,
52
+ scope: user.access_scopes,
53
+ associated_user_scope: user.access_scopes,
54
+ associated_user: associated_user
42
55
  )
43
56
  end
44
57
  end
@@ -1,2 +1,3 @@
1
1
  # frozen_string_literal: true
2
- require 'shopify_app/test_helpers/webhook_verification_helper'
2
+
3
+ require "shopify_app/test_helpers/webhook_verification_helper"
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  module TestHelpers
4
5
  module WebhookVerificationHelper
5
6
  def authorized_webhook_verification_headers!(params = {})
6
- digest = OpenSSL::Digest.new('sha256')
7
+ digest = OpenSSL::Digest.new("sha256")
7
8
  secret = ShopifyApp.configuration.secret
8
9
  valid_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, secret, params.to_query)).strip
9
- @request.headers['HTTP_X_SHOPIFY_HMAC_SHA256'] = valid_hmac
10
+ @request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"] = valid_hmac
10
11
  end
11
12
 
12
13
  def unauthorized_webhook_verification_headers!
13
- @request.headers['HTTP_X_SHOPIFY_HMAC_SHA256'] = "invalid_hmac"
14
+ @request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"] = "invalid_hmac"
14
15
  end
15
16
  end
16
17
  end