shopify_app 21.1.1 → 21.3.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +7 -8
  3. data/.github/workflows/stale.yml +1 -0
  4. data/.spin/rails/prepare-application +8 -0
  5. data/CHANGELOG.md +19 -8
  6. data/Gemfile +1 -0
  7. data/Gemfile.lock +106 -91
  8. data/README.md +19 -15
  9. data/SECURITY.md +1 -1
  10. data/app/controllers/concerns/shopify_app/authenticated.rb +4 -9
  11. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +3 -2
  12. data/app/controllers/concerns/shopify_app/ensure_has_session.rb +19 -0
  13. data/app/controllers/concerns/shopify_app/ensure_installed.rb +62 -0
  14. data/app/controllers/concerns/shopify_app/require_known_shop.rb +3 -38
  15. data/app/controllers/shopify_app/authenticated_controller.rb +1 -1
  16. data/app/controllers/shopify_app/callback_controller.rb +64 -27
  17. data/app/controllers/shopify_app/extension_verification_controller.rb +4 -1
  18. data/app/controllers/shopify_app/sessions_controller.rb +11 -2
  19. data/config/locales/ja.yml +1 -1
  20. data/docs/Troubleshooting.md +38 -2
  21. data/docs/Upgrading.md +40 -32
  22. data/docs/shopify_app/controller-concerns.md +48 -0
  23. data/docs/shopify_app/logging.md +21 -0
  24. data/docs/shopify_app/webhooks.md +13 -0
  25. data/lib/generators/shopify_app/add_app_uninstalled_job/add_app_uninstalled_job_generator.rb +15 -0
  26. data/lib/generators/shopify_app/add_app_uninstalled_job/templates/app_uninstalled_job.rb.tt +22 -0
  27. data/lib/generators/shopify_app/add_gdpr_jobs/add_gdpr_jobs_generator.rb +23 -0
  28. data/lib/generators/shopify_app/add_gdpr_jobs/templates/customers_data_request_job.rb.tt +22 -0
  29. data/lib/generators/shopify_app/add_gdpr_jobs/templates/customers_redact_job.rb.tt +22 -0
  30. data/lib/generators/shopify_app/add_gdpr_jobs/templates/shop_redact_job.rb.tt +22 -0
  31. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +1 -0
  32. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +2 -1
  33. data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +2 -1
  34. data/lib/generators/shopify_app/authenticated_controller/templates/authenticated_controller.rb +1 -1
  35. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +1 -1
  36. data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +1 -1
  37. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +8 -2
  38. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb +1 -1
  39. data/lib/generators/shopify_app/shopify_app_generator.rb +2 -0
  40. data/lib/shopify_app/access_scopes/noop_strategy.rb +4 -0
  41. data/lib/shopify_app/access_scopes/user_strategy.rb +5 -0
  42. data/lib/shopify_app/configuration.rb +11 -0
  43. data/lib/shopify_app/controller_concerns/ensure_billing.rb +3 -0
  44. data/lib/shopify_app/controller_concerns/itp.rb +5 -0
  45. data/lib/shopify_app/controller_concerns/login_protection.rb +56 -13
  46. data/lib/shopify_app/controller_concerns/redirect_for_embedded.rb +4 -1
  47. data/lib/shopify_app/controller_concerns/webhook_verification.rb +4 -1
  48. data/lib/shopify_app/logger.rb +28 -0
  49. data/lib/shopify_app/managers/scripttags_manager.rb +1 -0
  50. data/lib/shopify_app/managers/webhooks_manager.rb +6 -0
  51. data/lib/shopify_app/session/jwt.rb +1 -1
  52. data/lib/shopify_app/session/session_repository.rb +15 -4
  53. data/lib/shopify_app/version.rb +1 -1
  54. data/lib/shopify_app.rb +2 -0
  55. data/shopify_app.gemspec +2 -1
  56. data/yarn.lock +5 -5
  57. metadata +30 -4
@@ -0,0 +1,22 @@
1
+ class CustomersDataRequestJob < ActiveJob::Base
2
+ extend ShopifyAPI::Webhooks::Handler
3
+
4
+ class << self
5
+ def handle(topic:, shop:, body:)
6
+ perform_later(topic: topic, shop_domain: shop, webhook: body)
7
+ end
8
+ end
9
+
10
+ def perform(topic:, shop_domain:, webhook:)
11
+ shop = Shop.find_by(shopify_domain: shop_domain)
12
+
13
+ if shop.nil?
14
+ logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
15
+
16
+ raise ActiveRecord::RecordNotFound, "Shop Not Found"
17
+ end
18
+
19
+ shop.with_shopify_session do
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ class CustomersRedactJob < ActiveJob::Base
2
+ extend ShopifyAPI::Webhooks::Handler
3
+
4
+ class << self
5
+ def handle(topic:, shop:, body:)
6
+ perform_later(topic: topic, shop_domain: shop, webhook: body)
7
+ end
8
+ end
9
+
10
+ def perform(topic:, shop_domain:, webhook:)
11
+ shop = Shop.find_by(shopify_domain: shop_domain)
12
+
13
+ if shop.nil?
14
+ logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
15
+
16
+ raise ActiveRecord::RecordNotFound, "Shop Not Found"
17
+ end
18
+
19
+ shop.with_shopify_session do
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ class ShopRedactJob < ActiveJob::Base
2
+ extend ShopifyAPI::Webhooks::Handler
3
+
4
+ class << self
5
+ def handle(topic:, shop:, body:)
6
+ perform_later(topic: topic, shop_domain: shop, webhook: body)
7
+ end
8
+ end
9
+
10
+ def perform(topic:, shop_domain:, webhook:)
11
+ shop = Shop.find_by(shopify_domain: shop_domain)
12
+
13
+ if shop.nil?
14
+ logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
15
+
16
+ raise ActiveRecord::RecordNotFound, "Shop Not Found"
17
+ end
18
+
19
+ shop.with_shopify_session do
20
+ end
21
+ end
22
+ end
@@ -8,6 +8,7 @@ module ShopifyApp
8
8
  source_root File.expand_path("../templates", __FILE__)
9
9
 
10
10
  def generate_app_extension
11
+ ShopifyApp::Logger.deprecated("MarketingActivitiesController will be removed in an upcoming version", "22.0.0")
11
12
  template("marketing_activities_controller.rb", "app/controllers/marketing_activities_controller.rb")
12
13
  generate_routes
13
14
  end
@@ -55,7 +55,8 @@ class MarketingActivitiesController < ShopifyApp::ExtensionVerificationControlle
55
55
  request_id = params[:request_id]
56
56
  message = params[:message]
57
57
 
58
- Rails.logger.info("[Marketing Activity App Error Feedback] Request id: #{request_id}, message: #{message}")
58
+ ShopifyApp::Logger.info("[Marketing Activity App Error Feedback]"\
59
+ "Request id: #{request_id}, message: #{message}")
59
60
 
60
61
  render(json: {}, status: :ok)
61
62
  end
@@ -12,7 +12,8 @@ class <%= @job_class_name %> < ActiveJob::Base
12
12
 
13
13
  if shop.nil?
14
14
  logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
15
- return
15
+
16
+ raise ActiveRecord::RecordNotFound, "Shop Not Found"
16
17
  end
17
18
 
18
19
  shop.with_shopify_session do
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AuthenticatedController < ApplicationController
4
- include ShopifyApp::Authenticated
4
+ include ShopifyApp::EnsureHasSession
5
5
  end
@@ -24,7 +24,7 @@
24
24
  });
25
25
 
26
26
  var fetchProducts = function() {
27
- var headers = new Headers({ "Authorization": "Bearer " + window.sessionToken });
27
+ var headers = new Headers({ "Content-Type": "text/javascript", "Authorization": "Bearer " + window.sessionToken });
28
28
  return fetch("/products", { headers })
29
29
  .then(response => response.json())
30
30
  .then(data => {
@@ -2,7 +2,7 @@
2
2
 
3
3
  class HomeController < ApplicationController
4
4
  include ShopifyApp::EmbeddedApp
5
- include ShopifyApp::RequireKnownShop
5
+ include ShopifyApp::EnsureInstalled
6
6
  include ShopifyApp::ShopAccessScopesVerification
7
7
 
8
8
  def index
@@ -7,8 +7,14 @@ ShopifyApp.configure do |config|
7
7
  config.after_authenticate_job = false
8
8
  config.api_version = "<%= @api_version %>"
9
9
  config.shop_session_repository = 'Shop'
10
-
10
+ config.log_level = :info
11
11
  config.reauth_on_access_scope_changes = true
12
+ config.webhooks = [
13
+ { topic: "app/uninstalled", address: "webhooks/app_uninstalled"},
14
+ { topic: "customers/data_request", address: "webhooks/customers_data_request" },
15
+ { topic: "customer/redact", address: "webhooks/customers_redact"},
16
+ { topic: "shop/redact", address: "webhooks/shop_redact"}
17
+ ]
12
18
 
13
19
  config.api_key = ENV.fetch('SHOPIFY_API_KEY', '').presence
14
20
  config.secret = ENV.fetch('SHOPIFY_API_SECRET', '').presence
@@ -42,7 +48,7 @@ Rails.application.config.after_initialize do
42
48
  scope: ShopifyApp.configuration.scope,
43
49
  is_private: !ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', '').empty?,
44
50
  is_embedded: ShopifyApp.configuration.embedded_app,
45
- session_storage: ShopifyApp::SessionRepository,
51
+ log_level: :info,
46
52
  logger: Rails.logger,
47
53
  private_shop: ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', nil),
48
54
  user_agent_prefix: "ShopifyApp/#{ShopifyApp::VERSION}"
@@ -27,7 +27,7 @@ module Shopify
27
27
  private
28
28
 
29
29
  def log_error(message)
30
- Rails.logger.error(message)
30
+ ShopifyApp::Logger.error(message)
31
31
  end
32
32
 
33
33
  def no_access_token_error_message
@@ -9,6 +9,8 @@ module ShopifyApp
9
9
  end
10
10
 
11
11
  def run_all_generators
12
+ generate("shopify_app:add_app_uninstalled_job")
13
+ generate("shopify_app:add_gdpr_jobs")
12
14
  generate("shopify_app:install #{@opts.join(" ")}")
13
15
  generate("shopify_app:shop_model #{@opts.join(" ")}")
14
16
  generate("shopify_app:authenticated_controller")
@@ -7,6 +7,10 @@ module ShopifyApp
7
7
  def update_access_scopes?(*_args)
8
8
  false
9
9
  end
10
+
11
+ def covers_scopes?(*_args)
12
+ true
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -12,6 +12,11 @@ module ShopifyApp
12
12
  "#update_access_scopes? requires user_id or shopify_user_id parameter inputs")
13
13
  end
14
14
 
15
+ def covers_scopes?(current_shopify_session)
16
+ # NOTE: this not Ruby's `covers?` method, it is defined in ShopifyAPI::Auth::AuthScopes
17
+ current_shopify_session.scope.to_a.empty? || current_shopify_session.scope.covers?(ShopifyAPI::Context.scope)
18
+ end
19
+
15
20
  private
16
21
 
17
22
  def update_access_scopes_for_user_id?(user_id)
@@ -20,6 +20,7 @@ module ShopifyApp
20
20
  attr_accessor :api_version
21
21
 
22
22
  attr_accessor :reauth_on_access_scope_changes
23
+ attr_accessor :log_level
23
24
 
24
25
  # customise urls
25
26
  attr_accessor :root_url
@@ -82,7 +83,17 @@ module ShopifyApp
82
83
  ShopifyApp::AccessScopes::ShopStrategy
83
84
  end
84
85
 
86
+ def user_access_scopes_strategy=(class_name)
87
+ unless class_name.is_a?(String)
88
+ raise ConfigurationError, "Invalid user access scopes strategy - expected a string"
89
+ end
90
+
91
+ @user_access_scopes_strategy = class_name.safe_constantize
92
+ end
93
+
85
94
  def user_access_scopes_strategy
95
+ return @user_access_scopes_strategy if @user_access_scopes_strategy
96
+
86
97
  return ShopifyApp::AccessScopes::NoopStrategy unless reauth_on_access_scope_changes
87
98
 
88
99
  ShopifyApp::AccessScopes::UserStrategy
@@ -28,6 +28,7 @@ module ShopifyApp
28
28
  unless has_payment
29
29
  if request.xhr?
30
30
  add_top_level_redirection_headers(url: confirmation_url, ignore_response_code: true)
31
+ ShopifyApp::Logger.debug("Responding with 401 unauthorized")
31
32
  head(:unauthorized)
32
33
  else
33
34
  redirect_to(confirmation_url, allow_other_host: true)
@@ -55,6 +56,7 @@ module ShopifyApp
55
56
  end
56
57
 
57
58
  def has_subscription?(session)
59
+ ShopifyApp::Logger.debug("Checking if shop has subscription")
58
60
  response = run_query(session: session, query: RECURRING_PURCHASES_QUERY)
59
61
  subscriptions = response.body["data"]["currentAppInstallation"]["activeSubscriptions"]
60
62
 
@@ -70,6 +72,7 @@ module ShopifyApp
70
72
  end
71
73
 
72
74
  def has_one_time_payment?(session)
75
+ ShopifyApp::Logger.debug("Checking if has one time payment")
73
76
  purchases = nil
74
77
  end_cursor = nil
75
78
 
@@ -3,6 +3,11 @@
3
3
  module ShopifyApp
4
4
  # Cookie management helpers required for ITP implementation
5
5
  module Itp
6
+ extend ActiveSupport::Concern
7
+ included do
8
+ ShopifyApp::Logger.deprecated("Itp will be removed in an upcoming version", "22.0.0")
9
+ end
10
+
6
11
  private
7
12
 
8
13
  def set_test_cookie
@@ -5,11 +5,17 @@ require "browser_sniffer"
5
5
  module ShopifyApp
6
6
  module LoginProtection
7
7
  extend ActiveSupport::Concern
8
- include ShopifyApp::Itp
9
8
  include ShopifyApp::SanitizedParams
10
9
 
11
10
  included do
12
- after_action :set_test_cookie
11
+ if ancestors.include?(ShopifyApp::RequireKnownShop || ShopifyApp::EnsureInstalled)
12
+ message = <<~EOS
13
+ We detected the use of incompatible concerns (RequireKnownShop/EnsureInstalled and LoginProtection) in #{name},
14
+ which may lead to unpredictable behavior. In a future release of this library this will raise an error.
15
+ EOS
16
+ ShopifyApp::Logger.deprecated(message, "22.0.0")
17
+ end
18
+
13
19
  rescue_from ShopifyAPI::Errors::HttpResponseError, with: :handle_http_error
14
20
  end
15
21
 
@@ -18,20 +24,21 @@ module ShopifyApp
18
24
  def activate_shopify_session
19
25
  if current_shopify_session.blank?
20
26
  signal_access_token_required
27
+ ShopifyApp::Logger.debug("No session found, redirecting to login")
21
28
  return redirect_to_login
22
29
  end
23
30
 
24
- unless current_shopify_session.scope.to_a.empty? ||
25
- current_shopify_session.scope.covers?(ShopifyAPI::Context.scope)
26
-
31
+ unless ShopifyApp.configuration.user_access_scopes_strategy.covers_scopes?(current_shopify_session)
27
32
  clear_shopify_session
28
33
  return redirect_to_login
29
34
  end
30
35
 
31
36
  begin
37
+ ShopifyApp::Logger.debug("Activating Shopify session")
32
38
  ShopifyAPI::Context.activate_session(current_shopify_session)
33
39
  yield
34
40
  ensure
41
+ ShopifyApp::Logger.debug("Deactivating session")
35
42
  ShopifyAPI::Context.deactivate_session
36
43
  end
37
44
  end
@@ -39,14 +46,16 @@ module ShopifyApp
39
46
  def current_shopify_session
40
47
  @current_shopify_session ||= begin
41
48
  cookie_name = ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME
42
- ShopifyAPI::Utils::SessionUtils.load_current_session(
49
+ load_current_session(
43
50
  auth_header: request.headers["HTTP_AUTHORIZATION"],
44
51
  cookies: { cookie_name => cookies.encrypted[cookie_name] },
45
- is_online: user_session_expected?,
52
+ is_online: online_token_configured?,
46
53
  )
47
54
  rescue ShopifyAPI::Errors::CookieNotFoundError
55
+ ShopifyApp::Logger.warn("No cookies have been found - cookie name: #{cookie_name}")
48
56
  nil
49
57
  rescue ShopifyAPI::Errors::InvalidJwtTokenError
58
+ ShopifyApp::Logger.warn("Invalid JWT token for current Shopify session")
50
59
  nil
51
60
  end
52
61
  end
@@ -54,6 +63,7 @@ module ShopifyApp
54
63
  def login_again_if_different_user_or_shop
55
64
  return unless session_id_conflicts_with_params || session_shop_conflicts_with_params
56
65
 
66
+ ShopifyApp::Logger.debug("Clearing session and redirecting to login")
57
67
  clear_shopify_session
58
68
  redirect_to_login
59
69
  end
@@ -71,10 +81,13 @@ module ShopifyApp
71
81
 
72
82
  def add_top_level_redirection_headers(url: nil, ignore_response_code: false)
73
83
  if request.xhr? && (ignore_response_code || response.code.to_i == 401)
84
+ ShopifyApp::Logger.debug("Adding top level redirection headers")
74
85
  # Make sure the shop is set in the redirection URL
75
86
  unless params[:shop]
87
+ ShopifyApp::Logger.debug("Setting current shop session")
76
88
  params[:shop] = if current_shopify_session
77
89
  current_shopify_session.shop
90
+
78
91
  elsif (matches = request.headers["HTTP_AUTHORIZATION"]&.match(/^Bearer (.+)$/))
79
92
  jwt_payload = ShopifyAPI::Auth::JwtPayload.new(T.must(matches[1]))
80
93
  jwt_payload.shop
@@ -83,6 +96,7 @@ module ShopifyApp
83
96
 
84
97
  url ||= login_url_with_optional_shop
85
98
 
99
+ ShopifyApp::Logger.debug("Setting Reauthorize-Url to #{url}")
86
100
  response.set_header("X-Shopify-API-Request-Failure-Reauthorize", "1")
87
101
  response.set_header("X-Shopify-API-Request-Failure-Reauthorize-Url", url)
88
102
  end
@@ -103,8 +117,9 @@ module ShopifyApp
103
117
  end
104
118
 
105
119
  def redirect_to_login
106
- if request.xhr?
120
+ if requested_by_javascript?
107
121
  add_top_level_redirection_headers(ignore_response_code: true)
122
+ ShopifyApp::Logger.debug("Login redirect request is a XHR")
108
123
  head(:unauthorized)
109
124
  else
110
125
  if request.get?
@@ -117,12 +132,15 @@ module ShopifyApp
117
132
  query = query.merge(sanitized_params).to_query
118
133
  end
119
134
  session[:return_to] = query.blank? ? path.to_s : "#{path}?#{query}"
135
+ ShopifyApp::Logger.debug("Redirecting to #{login_url_with_optional_shop}")
120
136
  redirect_to(login_url_with_optional_shop)
121
137
  end
122
138
  end
123
139
 
124
140
  def close_session
125
141
  clear_shopify_session
142
+ ShopifyApp::Logger.debug("Closing session")
143
+ ShopifyApp::Logger.debug("Redirecting to #{login_url_with_optional_shop}")
126
144
  redirect_to(login_url_with_optional_shop)
127
145
  end
128
146
 
@@ -167,6 +185,10 @@ module ShopifyApp
167
185
  query_params[:host] ||= host
168
186
  end
169
187
 
188
+ if params[:access_scopes].present?
189
+ query_params[:scope] = params[:access_scopes].join(",")
190
+ end
191
+
170
192
  query_params[:top_level] = true if top_level
171
193
  query_params
172
194
  end
@@ -178,6 +200,8 @@ module ShopifyApp
178
200
 
179
201
  def fullpage_redirect_to(url)
180
202
  if ShopifyApp.configuration.embedded_app?
203
+ raise ::ShopifyApp::ShopifyDomainNotFound if current_shopify_domain.nil?
204
+
181
205
  render("shopify_app/shared/redirect", layout: false,
182
206
  locals: { url: url, current_shopify_domain: current_shopify_domain })
183
207
  else
@@ -187,13 +211,13 @@ module ShopifyApp
187
211
 
188
212
  def current_shopify_domain
189
213
  shopify_domain = sanitized_shop_name || current_shopify_session&.shop
190
-
191
- return shopify_domain if shopify_domain.present?
192
-
193
- raise ::ShopifyApp::ShopifyDomainNotFound
214
+ ShopifyApp::Logger.info("Installed store - #{shopify_domain} deduced from user session")
215
+ shopify_domain
194
216
  end
195
217
 
196
218
  def return_address
219
+ return base_return_address if current_shopify_domain.nil?
220
+
197
221
  return_address_with_params(shop: current_shopify_domain, host: host)
198
222
  rescue ::ShopifyApp::ShopifyDomainNotFound, ::ShopifyApp::ShopifyHostNotFound
199
223
  base_return_address
@@ -228,11 +252,30 @@ module ShopifyApp
228
252
  ShopifyApp::SessionRepository.retrieve_shop_session_by_shopify_domain(sanitize_shop_param(params))
229
253
  end
230
254
 
255
+ def online_token_configured?
256
+ !ShopifyApp.configuration.user_session_repository.blank? && ShopifyApp::SessionRepository.user_storage.present?
257
+ end
258
+
231
259
  def user_session_expected?
232
260
  return false if shop_session.nil?
233
261
  return false if ShopifyApp.configuration.shop_access_scopes_strategy.update_access_scopes?(shop_session.shop)
234
262
 
235
- !ShopifyApp.configuration.user_session_repository.blank? && ShopifyApp::SessionRepository.user_storage.present?
263
+ online_token_configured?
264
+ end
265
+
266
+ def load_current_session(auth_header: nil, cookies: nil, is_online: false)
267
+ return ShopifyAPI::Context.load_private_session if ShopifyAPI::Context.private?
268
+
269
+ session_id = ShopifyAPI::Utils::SessionUtils.current_session_id(auth_header, cookies, is_online)
270
+ return nil unless session_id
271
+
272
+ ShopifyApp::SessionRepository.load_session(session_id)
273
+ end
274
+
275
+ def requested_by_javascript?
276
+ request.xhr? ||
277
+ request.content_type == "text/javascript" ||
278
+ request.content_type == "application/javascript"
236
279
  end
237
280
  end
238
281
  end
@@ -16,7 +16,10 @@ module ShopifyApp
16
16
 
17
17
  def redirect_for_embedded
18
18
  # Don't actually redirect if we're already in the redirect route - we want the request to reach the FE
19
- redirect_to(redirect_uri_for_embedded) unless request.path == ShopifyApp.configuration.embedded_redirect_url
19
+ unless request.path == ShopifyApp.configuration.embedded_redirect_url
20
+ ShopifyApp::Logger.debug("Redirecting to #{redirect_uri_for_embedded}")
21
+ redirect_to(redirect_uri_for_embedded)
22
+ end
20
23
  end
21
24
 
22
25
  def redirect_uri_for_embedded
@@ -14,7 +14,10 @@ module ShopifyApp
14
14
 
15
15
  def verify_request
16
16
  data = request.raw_post
17
- return head(:unauthorized) unless hmac_valid?(data)
17
+ unless hmac_valid?(data)
18
+ ShopifyApp::Logger.debug("Webhook verification failed - HMAC invalid")
19
+ head(:unauthorized)
20
+ end
18
21
  end
19
22
 
20
23
  def shop_domain
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ class Logger < ShopifyAPI::Logger
5
+ class << self
6
+ def deprecated(message, version)
7
+ return unless enabled_for_log_level?(:warn)
8
+
9
+ raise ShopifyAPI::Errors::FeatureDeprecatedError unless valid_version(version)
10
+
11
+ ActiveSupport::Deprecation.warn("[#{version}] #{context(:warn)} #{message}")
12
+ end
13
+
14
+ private
15
+
16
+ def context(log_level)
17
+ current_shop = ShopifyAPI::Context.active_session&.shop || "Shop Not Found"
18
+ "[ ShopifyApp | #{log_level.to_s.upcase} | #{current_shop} ]"
19
+ end
20
+
21
+ def valid_version(version)
22
+ current_version = Gem::Version.create(ShopifyApp::VERSION)
23
+ deprecate_version = Gem::Version.create(version)
24
+ current_version < deprecate_version
25
+ end
26
+ end
27
+ end
28
+ end
@@ -24,6 +24,7 @@ module ShopifyApp
24
24
  attr_reader :required_scripttags, :shop_domain
25
25
 
26
26
  def initialize(scripttags, shop_domain)
27
+ ShopifyApp::Logger.deprecated("The ScripttagsManager will become deprecated in an upcoming version", "22.0.0")
27
28
  @required_scripttags = scripttags
28
29
  @shop_domain = shop_domain
29
30
  end
@@ -15,6 +15,8 @@ module ShopifyApp
15
15
  def create_webhooks(session:)
16
16
  return unless ShopifyApp.configuration.has_webhooks?
17
17
 
18
+ ShopifyApp::Logger.debug("Creating webhooks #{ShopifyApp.configuration.webhooks}")
19
+
18
20
  ShopifyAPI::Webhooks::Registry.register_all(session: session)
19
21
  end
20
22
 
@@ -23,12 +25,15 @@ module ShopifyApp
23
25
  return unless ShopifyApp.configuration.has_webhooks?
24
26
 
25
27
  add_registrations
28
+
29
+ ShopifyApp::Logger.debug("Recreating webhooks")
26
30
  ShopifyAPI::Webhooks::Registry.register_all(session: session)
27
31
  end
28
32
 
29
33
  def destroy_webhooks
30
34
  return unless ShopifyApp.configuration.has_webhooks?
31
35
 
36
+ ShopifyApp::Logger.debug("Destroying webhooks")
32
37
  ShopifyApp.configuration.webhooks.each do |attributes|
33
38
  ShopifyAPI::Webhooks::Registry.unregister(topic: attributes[:topic])
34
39
  end
@@ -37,6 +42,7 @@ module ShopifyApp
37
42
  def add_registrations
38
43
  return unless ShopifyApp.configuration.has_webhooks?
39
44
 
45
+ ShopifyApp::Logger.debug("Adding registrations to webhooks")
40
46
  ShopifyApp.configuration.webhooks.each do |attributes|
41
47
  webhook_path = path(attributes)
42
48
 
@@ -35,7 +35,7 @@ module ShopifyApp
35
35
  payload, _ = parse_token_data(ShopifyApp.configuration&.secret, ShopifyApp.configuration&.old_secret)
36
36
  @payload = validate_payload(payload)
37
37
  rescue *WARN_EXCEPTIONS => error
38
- Rails.logger.warn("[ShopifyApp::JWT] Failed to validate JWT: [#{error.class}] #{error}")
38
+ ShopifyApp::Logger.warn("Failed to validate JWT: [#{error.class}] #{error}")
39
39
  nil
40
40
  end
41
41
 
@@ -45,8 +45,11 @@ module ShopifyApp
45
45
  # ShopifyAPI::Auth::SessionStorage override
46
46
  def store_session(session)
47
47
  if session.online?
48
- user_storage.store(session, session.associated_user)
48
+ user = session.associated_user
49
+ ShopifyApp::Logger.debug("Storing online user session - session: #{session.id}")
50
+ user_storage.store(session, user)
49
51
  else
52
+ ShopifyApp::Logger.debug("Storing offline store session - session: #{session.id}")
50
53
  shop_storage.store(session)
51
54
  end
52
55
  end
@@ -55,9 +58,13 @@ module ShopifyApp
55
58
  def load_session(id)
56
59
  match = id.match(/^offline_(.*)/)
57
60
  if match
58
- retrieve_shop_session_by_shopify_domain(match[1])
61
+ domain = match[1]
62
+ ShopifyApp::Logger.debug("Loading session by domain - domain: #{domain}")
63
+ retrieve_shop_session_by_shopify_domain(domain)
59
64
  else
60
- retrieve_user_session_by_shopify_user_id(id.split("_").last)
65
+ user = id.split("_").last
66
+ ShopifyApp::Logger.debug("Loading session by user_id - user: #{user}")
67
+ retrieve_user_session_by_shopify_user_id(user)
61
68
  end
62
69
  end
63
70
 
@@ -66,9 +73,13 @@ module ShopifyApp
66
73
  match = id.match(/^offline_(.*)/)
67
74
 
68
75
  record = if match
76
+ domain = match[1]
77
+ ShopifyApp::Logger.debug("Destroying session by domain - domain: #{domain}")
69
78
  Shop.find_by(shopify_domain: match[1])
70
79
  else
71
- User.find_by(shopify_user_id: id.split("_").last)
80
+ shopify_user_id = id.split("_").last
81
+ ShopifyApp::Logger.debug("Destroying session by user - user_id: #{shopify_user_id}")
82
+ User.find_by(shopify_user_id: shopify_user_id)
72
83
  end
73
84
 
74
85
  record.destroy
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyApp
4
- VERSION = "21.1.1"
4
+ VERSION = "21.3.0"
5
5
  end
data/lib/shopify_app.rb CHANGED
@@ -37,6 +37,8 @@ module ShopifyApp
37
37
  # errors
38
38
  require "shopify_app/errors"
39
39
 
40
+ require "shopify_app/logger"
41
+
40
42
  # controller concerns
41
43
  require "shopify_app/controller_concerns/csrf_protection"
42
44
  require "shopify_app/controller_concerns/localization"
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", "~> 12.2")
22
+ s.add_runtime_dependency("shopify_api", "~> 12.3")
23
23
  s.add_runtime_dependency("sprockets-rails", ">= 2.0.0")
24
24
 
25
25
  s.add_development_dependency("byebug")
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  s.add_development_dependency("pry-stack_explorer")
31
31
  s.add_development_dependency("rake")
32
32
  s.add_development_dependency("rb-readline")
33
+ s.add_development_dependency("ruby-lsp")
33
34
  s.add_development_dependency("sqlite3", "~> 1.4")
34
35
  s.add_development_dependency("webmock")
35
36