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
@@ -4,180 +4,95 @@ module ShopifyApp
4
4
  # Performs login after OAuth completes
5
5
  class CallbackController < ActionController::Base
6
6
  include ShopifyApp::LoginProtection
7
+ include ShopifyApp::EnsureBilling
7
8
 
8
9
  def callback
9
- return respond_with_error if invalid_request?
10
+ begin
11
+ filtered_params = request.parameters.symbolize_keys.slice(:code, :shop, :timestamp, :state, :host, :hmac)
12
+
13
+ auth_result = ShopifyAPI::Auth::Oauth.validate_auth_callback(
14
+ cookies: {
15
+ ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME =>
16
+ cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME],
17
+ },
18
+ auth_query: ShopifyAPI::Auth::Oauth::AuthQuery.new(**filtered_params)
19
+ )
20
+ rescue
21
+ return respond_with_error
22
+ end
23
+
24
+ cookies.encrypted[auth_result[:cookie].name] = {
25
+ expires: auth_result[:cookie].expires,
26
+ secure: true,
27
+ http_only: true,
28
+ value: auth_result[:cookie].value,
29
+ }
10
30
 
11
- store_access_token_and_build_session
31
+ session[:shopify_user_id] = auth_result[:session].associated_user.id if auth_result[:session].online?
12
32
 
13
- if start_user_token_flow?
33
+ if start_user_token_flow?(auth_result[:session])
14
34
  return respond_with_user_token_flow
15
35
  end
16
36
 
17
- perform_post_authenticate_jobs
37
+ perform_post_authenticate_jobs(auth_result[:session])
38
+ has_payment = check_billing(auth_result[:session])
18
39
 
19
- respond_successfully
40
+ respond_successfully if has_payment
20
41
  end
21
42
 
22
43
  private
23
44
 
24
45
  def respond_successfully
25
- if jwt_request?
26
- head(:ok)
27
- else
28
- redirect_to(return_address)
29
- end
30
- end
31
-
32
- def respond_with_user_token_flow
33
- redirect_to(login_url_with_optional_shop)
34
- end
35
-
36
- def store_access_token_and_build_session
37
- if native_browser_request?
38
- reset_session_options
39
- end
40
- set_shopify_session
41
- end
42
-
43
- def invalid_request?
44
- return true unless auth_hash
45
-
46
- jwt_request? && !valid_jwt_auth?
47
- end
48
-
49
- def native_browser_request?
50
- !jwt_request?
51
- end
52
-
53
- def perform_post_authenticate_jobs
54
- install_webhooks
55
- install_scripttags
56
- perform_after_authenticate_job
46
+ redirect_to(return_address)
57
47
  end
58
48
 
59
49
  def respond_with_error
60
- if jwt_request?
61
- head(:unauthorized)
62
- else
63
- flash[:error] = I18n.t('could_not_log_in')
64
- redirect_to(login_url_with_optional_shop)
65
- end
50
+ flash[:error] = I18n.t("could_not_log_in")
51
+ redirect_to(login_url_with_optional_shop)
66
52
  end
67
53
 
68
- # Override user_session_by_cookie from LoginProtection to bypass allow_cookie_authentication
69
- # setting check because session cookies are justified at top level
70
- def user_session_by_cookie
71
- return unless session[:user_id].present?
72
- ShopifyApp::SessionRepository.retrieve_user_session(session[:user_id])
54
+ def respond_with_user_token_flow
55
+ redirect_to(login_url_with_optional_shop)
73
56
  end
74
57
 
75
- def start_user_token_flow?
76
- if jwt_request?
77
- false
78
- else
79
- return false unless ShopifyApp::SessionRepository.user_storage.present?
80
- update_user_access_scopes?
81
- end
58
+ def start_user_token_flow?(shopify_session)
59
+ return false unless ShopifyApp::SessionRepository.user_storage.present?
60
+ return false if shopify_session.online?
61
+ update_user_access_scopes?
82
62
  end
83
63
 
84
64
  def update_user_access_scopes?
85
- return true if user_session.blank?
86
- user_access_scopes_strategy.update_access_scopes?(user_id: session[:user_id])
65
+ return true if session[:shopify_user_id].nil?
66
+ user_access_scopes_strategy.update_access_scopes?(shopify_user_id: session[:shopify_user_id])
87
67
  end
88
68
 
89
69
  def user_access_scopes_strategy
90
70
  ShopifyApp.configuration.user_access_scopes_strategy
91
71
  end
92
72
 
93
- def jwt_request?
94
- jwt_shopify_domain || jwt_shopify_user_id
95
- end
96
-
97
- def valid_jwt_auth?
98
- auth_hash && jwt_shopify_domain == shop_name && jwt_shopify_user_id == associated_user_id
73
+ def perform_post_authenticate_jobs(session)
74
+ install_webhooks(session)
75
+ install_scripttags(session)
76
+ perform_after_authenticate_job(session)
99
77
  end
100
78
 
101
- def auth_hash
102
- request.env['omniauth.auth']
103
- end
104
-
105
- def shop_name
106
- auth_hash.uid
107
- end
108
-
109
- def offline_access_token
110
- ShopifyApp::SessionRepository.retrieve_shop_session_by_shopify_domain(shop_name)&.token
111
- end
112
-
113
- def online_access_token
114
- ShopifyApp::SessionRepository.retrieve_user_session_by_shopify_user_id(associated_user_id)&.token
115
- end
116
-
117
- def associated_user
118
- return unless auth_hash.dig('extra', 'associated_user').present?
119
-
120
- auth_hash['extra']['associated_user'].merge('scope' => auth_hash['extra']['associated_user_scope'])
121
- end
122
-
123
- def associated_user_id
124
- associated_user && associated_user['id']
125
- end
126
-
127
- def token
128
- auth_hash['credentials']['token']
129
- end
130
-
131
- def access_scopes
132
- auth_hash.dig('extra', 'scope')
133
- end
134
-
135
- def reset_session_options
136
- request.session_options[:renew] = true
137
- session.delete(:_csrf_token)
138
- end
139
-
140
- def set_shopify_session
141
- session_store = ShopifyAPI::Session.new(
142
- domain: shop_name,
143
- token: token,
144
- api_version: ShopifyApp.configuration.api_version,
145
- access_scopes: access_scopes
146
- )
147
-
148
- session[:shopify_user] = associated_user
149
- if session[:shopify_user].present?
150
- session[:shop_id] = nil if shop_session && shop_session.domain != shop_name
151
- session[:user_id] = ShopifyApp::SessionRepository.store_user_session(session_store, associated_user)
152
- else
153
- session[:shop_id] = ShopifyApp::SessionRepository.store_shop_session(session_store)
154
- session[:user_id] = nil if user_session && user_session.domain != shop_name
155
- end
156
- session[:shopify_domain] = shop_name
157
- session[:user_session] = auth_hash&.extra&.session
158
- end
159
-
160
- def install_webhooks
79
+ def install_webhooks(session)
161
80
  return unless ShopifyApp.configuration.has_webhooks?
162
81
 
163
- WebhooksManager.queue(
164
- shop_name,
165
- offline_access_token || online_access_token,
166
- ShopifyApp.configuration.webhooks
167
- )
82
+ WebhooksManager.queue(session.shop, session.access_token)
168
83
  end
169
84
 
170
- def install_scripttags
85
+ def install_scripttags(session)
171
86
  return unless ShopifyApp.configuration.has_scripttags?
172
87
 
173
88
  ScripttagsManager.queue(
174
- shop_name,
175
- offline_access_token || online_access_token,
89
+ session.shop,
90
+ session.access_token,
176
91
  ShopifyApp.configuration.scripttags
177
92
  )
178
93
  end
179
94
 
180
- def perform_after_authenticate_job
95
+ def perform_after_authenticate_job(session)
181
96
  config = ShopifyApp.configuration.after_authenticate_job
182
97
 
183
98
  return unless config && config[:job].present?
@@ -186,9 +101,9 @@ module ShopifyApp
186
101
  job = job.constantize if job.is_a?(String)
187
102
 
188
103
  if config[:inline] == true
189
- job.perform_now(shop_domain: session[:shopify_domain])
104
+ job.perform_now(shop_domain: session.shop)
190
105
  else
191
- job.perform_later(shop_domain: session[:shopify_domain])
106
+ job.perform_later(shop_domain: session.shop)
192
107
  end
193
108
  end
194
109
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class SessionsController < ActionController::Base
4
5
  include ShopifyApp::LoginProtection
@@ -6,7 +7,7 @@ module ShopifyApp
6
7
  layout false, only: :new
7
8
 
8
9
  after_action only: [:new, :create] do |controller|
9
- controller.response.headers.except!('X-Frame-Options')
10
+ controller.response.headers.except!("X-Frame-Options")
10
11
  end
11
12
 
12
13
  def new
@@ -17,41 +18,14 @@ module ShopifyApp
17
18
  authenticate
18
19
  end
19
20
 
20
- def enable_cookies
21
- return unless validate_shop_presence
22
-
23
- render(:enable_cookies, layout: false, locals: {
24
- does_not_have_storage_access_url: top_level_interaction_path(
25
- shop: sanitized_shop_name,
26
- return_to: params[:return_to]
27
- ),
28
- has_storage_access_url: login_url_with_optional_shop(top_level: true),
29
- app_target_url: granted_storage_access_path(
30
- shop: sanitized_shop_name,
31
- return_to: params[:return_to]
32
- ),
33
- current_shopify_domain: current_shopify_domain,
34
- })
35
- end
36
-
37
21
  def top_level_interaction
38
22
  @url = login_url_with_optional_shop(top_level: true)
39
23
  validate_shop_presence
40
24
  end
41
25
 
42
- def granted_storage_access
43
- return unless validate_shop_presence
44
-
45
- session['shopify.granted_storage_access'] = true
46
-
47
- copy_return_to_param_to_session
48
-
49
- redirect_to(return_address_with_params({ shop: @shop }))
50
- end
51
-
52
26
  def destroy
53
27
  reset_session
54
- flash[:notice] = I18n.t('.logged_out')
28
+ flash[:notice] = I18n.t(".logged_out")
55
29
  redirect_to(login_url_with_optional_shop)
56
30
  end
57
31
 
@@ -59,69 +33,33 @@ module ShopifyApp
59
33
 
60
34
  def authenticate
61
35
  return render_invalid_shop_error unless sanitized_shop_name.present?
62
- session['shopify.omniauth_params'] = { shop: sanitized_shop_name }
63
36
 
64
37
  copy_return_to_param_to_session
65
38
 
66
- set_user_tokens_option
67
-
68
- if user_agent_can_partition_cookies
69
- authenticate_with_partitioning
39
+ if top_level?
40
+ start_oauth
70
41
  else
71
- authenticate_normally
42
+ redirect_auth_to_top_level
72
43
  end
73
44
  end
74
45
 
75
- def authenticate_normally
76
- if request_storage_access?
77
- redirect_to_request_storage_access
78
- elsif authenticate_in_context?
79
- authenticate_in_context
80
- else
81
- authenticate_at_top_level
82
- end
83
- end
84
-
85
- def authenticate_with_partitioning
86
- if session['shopify.cookies_persist']
87
- clear_top_level_oauth_cookie
88
- authenticate_in_context
89
- else
90
- set_top_level_oauth_cookie
91
- enable_cookie_access
92
- end
93
- end
94
-
95
- # Override shop_session_by_cookie from LoginProtection to bypass allow_cookie_authentication
96
- # setting check because session cookies are justified at top level
97
- def shop_session_by_cookie
98
- return unless session[:shop_id].present?
99
- ShopifyApp::SessionRepository.retrieve_shop_session(session[:shop_id])
100
- end
101
-
102
- # rubocop:disable Lint/SuppressedException
103
- def set_user_tokens_option
104
- current_shop_session = shop_session
46
+ def start_oauth
47
+ callback_url = ShopifyApp.configuration.login_callback_url.gsub(%r{^/}, "")
105
48
 
106
- if current_shop_session.blank?
107
- session[:user_tokens] = false
108
- return
109
- end
110
-
111
- session[:user_tokens] = ShopifyApp::SessionRepository.user_storage.present?
49
+ auth_attributes = ShopifyAPI::Auth::Oauth.begin_auth(
50
+ shop: sanitized_shop_name,
51
+ redirect_path: "/#{callback_url}",
52
+ is_online: user_session_expected?
53
+ )
54
+ cookies.encrypted[auth_attributes[:cookie].name] = {
55
+ expires: auth_attributes[:cookie].expires,
56
+ secure: true,
57
+ http_only: true,
58
+ value: auth_attributes[:cookie].value,
59
+ }
112
60
 
113
- ShopifyAPI::Session.temp(
114
- domain: current_shop_session.domain,
115
- token: current_shop_session.token,
116
- api_version: current_shop_session.api_version
117
- ) do
118
- ShopifyAPI::Metafield.find(:token_validity_bogus_check)
119
- end
120
- rescue ActiveResource::UnauthorizedAccess
121
- session[:user_tokens] = false
122
- rescue StandardError
61
+ redirect_to(auth_attributes[:auth_route], allow_other_host: true)
123
62
  end
124
- # rubocop:enable Lint/SuppressedException
125
63
 
126
64
  def validate_shop_presence
127
65
  @shop = sanitized_shop_name
@@ -134,64 +72,21 @@ module ShopifyApp
134
72
  end
135
73
 
136
74
  def copy_return_to_param_to_session
137
- session[:return_to] = RedirectSafely.make_safe(params[:return_to], '/') if params[:return_to]
75
+ session[:return_to] = RedirectSafely.make_safe(params[:return_to], "/") if params[:return_to]
138
76
  end
139
77
 
140
78
  def render_invalid_shop_error
141
- flash[:error] = I18n.t('invalid_shop_url')
79
+ flash[:error] = I18n.t("invalid_shop_url")
142
80
  redirect_to(return_address)
143
81
  end
144
82
 
145
- def enable_cookie_access
146
- fullpage_redirect_to(enable_cookies_path(
147
- shop: sanitized_shop_name,
148
- return_to: session[:return_to]
149
- ))
150
- end
151
-
152
- def authenticate_in_context
153
- post_redirect_to_auth_shopify
154
- end
155
-
156
- def post_redirect_to_auth_shopify
157
- render('shopify_app/shared/post_redirect_to_auth_shopify', layout: false)
158
- end
159
-
160
- def authenticate_at_top_level
161
- fullpage_redirect_to(login_url_with_optional_shop(top_level: true))
162
- end
163
-
164
- def authenticate_in_context?
83
+ def top_level?
165
84
  return true unless ShopifyApp.configuration.embedded_app?
166
- params[:top_level]
85
+ !params[:top_level].nil?
167
86
  end
168
87
 
169
- def request_storage_access?
170
- return false unless ShopifyApp.configuration.embedded_app?
171
- return false if params[:top_level]
172
- return false if user_agent_is_mobile
173
- return false if user_agent_is_pos
174
-
175
- !session['shopify.granted_storage_access']
176
- end
177
-
178
- def redirect_to_request_storage_access
179
- render(
180
- :request_storage_access,
181
- layout: false,
182
- locals: {
183
- does_not_have_storage_access_url: top_level_interaction_path(
184
- shop: sanitized_shop_name,
185
- return_to: session[:return_to]
186
- ),
187
- has_storage_access_url: login_url_with_optional_shop(top_level: true),
188
- app_target_url: granted_storage_access_path(
189
- shop: sanitized_shop_name,
190
- return_to: session[:return_to]
191
- ),
192
- current_shopify_domain: current_shopify_domain,
193
- }
194
- )
88
+ def redirect_auth_to_top_level
89
+ fullpage_redirect_to(login_url_with_optional_shop(top_level: true))
195
90
  end
196
91
  end
197
92
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ShopifyApp
3
4
  class MissingWebhookJobError < StandardError; end
4
5
 
@@ -7,31 +8,11 @@ module ShopifyApp
7
8
 
8
9
  def receive
9
10
  params.permit!
10
- job_args = { shop_domain: shop_domain, webhook: webhook_params.to_h }
11
- webhook_job_klass.perform_later(job_args)
12
- head(:ok)
13
- end
14
-
15
- private
16
11
 
17
- def webhook_params
18
- params.except(:controller, :action, :type)
19
- end
20
-
21
- def webhook_job_klass
22
- webhook_job_klass_name.safe_constantize || raise(ShopifyApp::MissingWebhookJobError)
23
- end
24
-
25
- def webhook_job_klass_name(type = webhook_type)
26
- [webhook_namespace, "#{type}_job"].compact.join('/').classify
27
- end
28
-
29
- def webhook_type
30
- params[:type]
31
- end
32
-
33
- def webhook_namespace
34
- ShopifyApp.configuration.webhook_jobs_namespace
12
+ ShopifyAPI::Webhooks::Registry.process(
13
+ ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
14
+ )
15
+ head(:ok)
35
16
  end
36
17
  end
37
18
  end
@@ -17,7 +17,7 @@
17
17
 
18
18
  <%= javascript_include_tag('shopify_app/enable_cookies', crossorigin: 'anonymous', integrity: true) %>
19
19
  </head>
20
- <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="https://<%= @shop %>" data-redirect-url="<%= @url %>">
20
+ <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="<%= @shop %>" data-redirect-url="<%= @url %>" data-host="<%= params[:host] %>" data-return-to="<%= params[:return_to] %>">
21
21
  <%=
22
22
  content_tag(
23
23
  :div, nil,
@@ -15,19 +15,19 @@
15
15
  }
16
16
  </style>
17
17
  </head>
18
- <body>
18
+ <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="<%= current_shopify_domain %>" data-host="<%= params[:host] %>" data-return-to="<%= params[:return_to] %>">
19
19
  <%=
20
20
  content_tag(:div, nil,
21
- id: 'redirection-target',
22
- data: {
23
- target: {
24
- myshopifyUrl: "https://#{current_shopify_domain}",
25
- hasStorageAccessUrl: "#{has_storage_access_url}",
26
- doesNotHaveStorageAccessUrl: "#{does_not_have_storage_access_url}",
27
- appTargetUrl: "#{app_target_url}"
28
- },
29
- },
30
- )
21
+ id: 'redirection-target',
22
+ data: {
23
+ target: {
24
+ myshopifyUrl: "https://#{current_shopify_domain}",
25
+ hasStorageAccessUrl: "#{has_storage_access_url}",
26
+ doesNotHaveStorageAccessUrl: "#{does_not_have_storage_access_url}",
27
+ appTargetUrl: "#{app_target_url}"
28
+ },
29
+ },
30
+ )
31
31
  %>
32
32
  <main id="RequestStorageAccess">
33
33
  <div class="Polaris-Page">
@@ -18,7 +18,7 @@
18
18
 
19
19
  <%= javascript_include_tag('shopify_app/top_level', crossorigin: 'anonymous', integrity: true) %>
20
20
  </head>
21
- <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="https://<%= @shop %>" data-redirect-url="<%= @url %>">
21
+ <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="https://<%= @shop %>" data-host="<%= @host %>" data-redirect-url="<%= @url %>">
22
22
  <main id="TopLevelInteractionContent">
23
23
  <div class="Polaris-Page">
24
24
  <div class="Polaris-Page__Content">
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="<%= I18n.locale %>">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -7,7 +7,7 @@
7
7
  <title>Redirecting…</title>
8
8
  <%= javascript_include_tag('shopify_app/redirect', crossorigin: 'anonymous', integrity: true) %>
9
9
  </head>
10
- <body>
10
+ <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="<%= current_shopify_domain %>" data-host="<%= params[:host] %>" >
11
11
  <%=
12
12
  content_tag(:div, nil,
13
13
  id: 'redirection-target',
@@ -11,6 +11,6 @@ zh-CN:
11
11
  top_level_interaction_body: 您的浏览器要求类似 %{app} 的应用向您申请访问 Cookie,之后 Shopify 才能为您打开它。
12
12
  top_level_interaction_action: 继续
13
13
  request_storage_access_heading: "%{app} 需要访问 Cookie"
14
- request_storage_access_body: 这使此应用能够通过暂时存储您的个人信息来验证您的身份。单击继续并启用 Cookie 以使用此应用。
14
+ request_storage_access_body: 这使此应用能够通过暂时存储您的个人信息来验证您的身份。点击继续并启用 Cookie 以使用此应用。
15
15
  request_storage_access_footer: Cookie 将在 30 天后过期。
16
16
  request_storage_access_action: 继续
data/config/routes.rb CHANGED
@@ -1,23 +1,31 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  ShopifyApp::Engine.routes.draw do
4
+ login_url = ShopifyApp.configuration.login_url.gsub(/^#{ShopifyApp.configuration.root_url}/, "")
5
+ login_callback_url = ShopifyApp.configuration.login_callback_url.gsub(/^#{ShopifyApp.configuration.root_url}/, "")
6
+
3
7
  controller :sessions do
4
- get 'login' => :new, :as => :login
5
- post 'login' => :create, :as => :authenticate
6
- get 'enable_cookies' => :enable_cookies, :as => :enable_cookies
7
- get 'top_level_interaction' =>
8
- :top_level_interaction,
9
- :as => :top_level_interaction
10
- get 'granted_storage_access' =>
11
- :granted_storage_access,
12
- :as => :granted_storage_access
13
- get 'logout' => :destroy, :as => :logout
8
+ get login_url => :new, :as => :login
9
+ post login_url => :create, :as => :authenticate
10
+ get "logout" => :destroy, :as => :logout
11
+
12
+ # Kept to prevent apps relying on these routes from breaking
13
+ if login_url.gsub(%r{^/}, "") != "login"
14
+ get "login" => :new, :as => :default_login
15
+ post "login" => :create, :as => :default_authenticate
16
+ end
14
17
  end
15
18
 
16
19
  controller :callback do
17
- get 'auth/shopify/callback' => :callback
20
+ get login_callback_url => :callback
21
+
22
+ # Kept to prevent apps relying on these routes from breaking
23
+ if login_callback_url.gsub(%r{^/}, "") != "auth/shopify/callback"
24
+ get "auth/shopify/callback" => :default_callback
25
+ end
18
26
  end
19
27
 
20
28
  namespace :webhooks do
21
- post ':type' => :receive
29
+ post ":type" => :receive
22
30
  end
23
31
  end
@@ -87,9 +87,6 @@ Edit `config/initializer/shopify_app.rb` and ensure the following configurations
87
87
  ```diff
88
88
  + config.embedded_app = true
89
89
 
90
- + config.allow_jwt_authentication = true
91
- + config.allow_cookie_authentication = false
92
-
93
90
  # This line should already exist if you're using shopify_app gem 13.x.x+
94
91
  + config.shop_session_repository = 'Shop'
95
92
  ```