shopify_app 21.0.0 → 22.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/ISSUE_TEMPLATE/ENHANCEMENT.md +9 -0
  4. data/.github/ISSUE_TEMPLATE/bug-report.md +30 -47
  5. data/.github/ISSUE_TEMPLATE/feature-request.md +5 -29
  6. data/.github/workflows/build.yml +11 -12
  7. data/.github/workflows/release.yml +2 -2
  8. data/.github/workflows/remove-labels-on-activity.yml +1 -1
  9. data/.github/workflows/rubocop.yml +2 -3
  10. data/.nvmrc +1 -1
  11. data/.rubocop.yml +2 -1
  12. data/.ruby-version +1 -1
  13. data/.spin/rails/prepare-application +8 -0
  14. data/CHANGELOG.md +173 -7
  15. data/CODE_OF_CONDUCT.md +46 -0
  16. data/CONTRIBUTING.md +16 -6
  17. data/Gemfile +1 -0
  18. data/Gemfile.lock +160 -121
  19. data/README.md +67 -19
  20. data/SECURITY.md +1 -1
  21. data/app/assets/javascripts/shopify_app/redirect.js +3 -10
  22. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +9 -4
  23. data/app/controllers/concerns/shopify_app/ensure_has_session.rb +25 -0
  24. data/app/controllers/concerns/shopify_app/ensure_installed.rb +84 -0
  25. data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +5 -1
  26. data/app/controllers/shopify_app/authenticated_controller.rb +1 -1
  27. data/app/controllers/shopify_app/callback_controller.rb +101 -39
  28. data/app/controllers/shopify_app/extension_verification_controller.rb +4 -1
  29. data/app/controllers/shopify_app/sessions_controller.rb +37 -7
  30. data/app/controllers/shopify_app/webhooks_controller.rb +1 -1
  31. data/app/views/shopify_app/layouts/app_bridge.html.erb +17 -0
  32. data/app/views/shopify_app/sessions/patch_shopify_id_token.html.erb +0 -0
  33. data/app/views/shopify_app/shared/redirect.html.erb +10 -1
  34. data/config/locales/cs.yml +0 -18
  35. data/config/locales/da.yml +0 -15
  36. data/config/locales/de.yml +0 -17
  37. data/config/locales/en.yml +0 -11
  38. data/config/locales/es.yml +0 -17
  39. data/config/locales/fi.yml +0 -15
  40. data/config/locales/fr.yml +0 -18
  41. data/config/locales/it.yml +0 -16
  42. data/config/locales/ja.yml +0 -12
  43. data/config/locales/ko.yml +0 -14
  44. data/config/locales/nb.yml +0 -16
  45. data/config/locales/nl.yml +0 -16
  46. data/config/locales/pl.yml +0 -16
  47. data/config/locales/pt-BR.yml +0 -16
  48. data/config/locales/pt-PT.yml +0 -17
  49. data/config/locales/sv.yml +0 -16
  50. data/config/locales/th.yml +0 -15
  51. data/config/locales/tr.yml +0 -17
  52. data/config/locales/vi.yml +0 -17
  53. data/config/locales/zh-CN.yml +0 -11
  54. data/config/locales/zh-TW.yml +0 -11
  55. data/config/routes.rb +2 -1
  56. data/docs/Quickstart.md +14 -5
  57. data/docs/Troubleshooting.md +38 -25
  58. data/docs/Upgrading.md +103 -32
  59. data/docs/shopify_app/authentication.md +179 -58
  60. data/docs/shopify_app/controller-concerns.md +89 -0
  61. data/docs/shopify_app/engine.md +2 -11
  62. data/docs/shopify_app/generators.md +2 -2
  63. data/docs/shopify_app/logging.md +21 -0
  64. data/docs/shopify_app/sessions.md +358 -0
  65. data/docs/shopify_app/testing.md +32 -10
  66. data/docs/shopify_app/webhooks.md +97 -7
  67. data/karma.conf.js +6 -4
  68. data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +6 -3
  69. data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -1
  70. data/lib/generators/shopify_app/add_app_uninstalled_job/add_app_uninstalled_job_generator.rb +15 -0
  71. data/lib/generators/shopify_app/add_app_uninstalled_job/templates/app_uninstalled_job.rb.tt +22 -0
  72. data/lib/generators/shopify_app/add_declarative_webhook/add_declarative_webhook_generator.rb +53 -0
  73. data/lib/generators/shopify_app/add_declarative_webhook/templates/webhook_controller.rb.tt +13 -0
  74. data/lib/generators/shopify_app/add_declarative_webhook/templates/webhook_job.rb.tt +15 -0
  75. data/lib/generators/shopify_app/add_privacy_jobs/add_privacy_jobs_generator.rb +23 -0
  76. data/lib/generators/shopify_app/add_privacy_jobs/templates/customers_data_request_job.rb.tt +22 -0
  77. data/lib/generators/shopify_app/add_privacy_jobs/templates/customers_redact_job.rb.tt +22 -0
  78. data/lib/generators/shopify_app/add_privacy_jobs/templates/shop_redact_job.rb.tt +22 -0
  79. data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +8 -3
  80. data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +4 -2
  81. data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +1 -1
  82. data/lib/generators/shopify_app/authenticated_controller/templates/authenticated_controller.rb +1 -1
  83. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +1 -1
  84. data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +1 -1
  85. data/lib/generators/shopify_app/install/install_generator.rb +4 -4
  86. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +13 -3
  87. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token.rake +1 -1
  88. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb +1 -1
  89. data/lib/generators/shopify_app/routes/routes_generator.rb +1 -1
  90. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +1 -1
  91. data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_scopes_column.erb +1 -1
  92. data/lib/generators/shopify_app/shopify_app_generator.rb +2 -0
  93. data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_access_scopes_column.erb +1 -1
  94. data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_expires_at_column.erb +5 -0
  95. data/lib/generators/shopify_app/user_model/user_model_generator.rb +21 -1
  96. data/lib/shopify_app/access_scopes/noop_strategy.rb +4 -0
  97. data/lib/shopify_app/access_scopes/user_strategy.rb +9 -2
  98. data/lib/shopify_app/admin_api/with_token_refetch.rb +27 -0
  99. data/lib/shopify_app/auth/post_authenticate_tasks.rb +48 -0
  100. data/lib/shopify_app/auth/token_exchange.rb +73 -0
  101. data/lib/shopify_app/configuration.rb +82 -1
  102. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +3 -3
  103. data/lib/shopify_app/controller_concerns/csrf_protection.rb +2 -1
  104. data/lib/shopify_app/controller_concerns/embedded_app.rb +42 -3
  105. data/lib/shopify_app/controller_concerns/ensure_billing.rb +28 -12
  106. data/lib/shopify_app/controller_concerns/frame_ancestors.rb +1 -1
  107. data/lib/shopify_app/controller_concerns/localization.rb +11 -8
  108. data/lib/shopify_app/controller_concerns/login_protection.rb +83 -38
  109. data/lib/shopify_app/controller_concerns/payload_verification.rb +1 -1
  110. data/lib/shopify_app/controller_concerns/redirect_for_embedded.rb +15 -3
  111. data/lib/shopify_app/controller_concerns/sanitized_params.rb +5 -0
  112. data/lib/shopify_app/controller_concerns/token_exchange.rb +111 -0
  113. data/lib/shopify_app/controller_concerns/webhook_verification.rb +4 -1
  114. data/lib/shopify_app/controller_concerns/with_shopify_id_token.rb +48 -0
  115. data/lib/shopify_app/engine.rb +7 -8
  116. data/lib/shopify_app/logger.rb +28 -0
  117. data/lib/shopify_app/managers/webhooks_manager.rb +20 -10
  118. data/lib/shopify_app/middleware/jwt_middleware.rb +13 -9
  119. data/lib/shopify_app/session/in_memory_user_session_store.rb +1 -1
  120. data/lib/shopify_app/session/jwt.rb +11 -2
  121. data/lib/shopify_app/session/session_repository.rb +66 -14
  122. data/lib/shopify_app/session/session_storage.rb +2 -2
  123. data/lib/shopify_app/session/shop_session_storage.rb +5 -1
  124. data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +5 -1
  125. data/lib/shopify_app/session/user_session_storage.rb +6 -2
  126. data/lib/shopify_app/session/user_session_storage_with_scopes.rb +27 -2
  127. data/lib/shopify_app/test_helpers/all.rb +1 -0
  128. data/lib/shopify_app/test_helpers/shopify_session_helper.rb +16 -0
  129. data/lib/shopify_app/utils.rb +82 -20
  130. data/lib/shopify_app/version.rb +1 -1
  131. data/lib/shopify_app.rb +12 -3
  132. data/package.json +5 -6
  133. data/service.yml +0 -2
  134. data/shopify_app.gemspec +6 -5
  135. data/translation.yml +1 -0
  136. data/yarn.lock +2139 -3910
  137. metadata +78 -58
  138. data/.github/workflows/stale.yml +0 -31
  139. data/app/assets/images/storage_access.svg +0 -1
  140. data/app/assets/javascripts/shopify_app/app_bridge_3.1.1.js +0 -10
  141. data/app/assets/javascripts/shopify_app/app_bridge_redirect.js +0 -22
  142. data/app/assets/javascripts/shopify_app/app_bridge_utils_3.1.1.js +0 -1
  143. data/app/assets/javascripts/shopify_app/enable_cookies.js +0 -3
  144. data/app/assets/javascripts/shopify_app/itp_helper.js +0 -40
  145. data/app/assets/javascripts/shopify_app/partition_cookies.js +0 -8
  146. data/app/assets/javascripts/shopify_app/post_redirect.js +0 -9
  147. data/app/assets/javascripts/shopify_app/request_storage_access.js +0 -3
  148. data/app/assets/javascripts/shopify_app/storage_access.js +0 -148
  149. data/app/assets/javascripts/shopify_app/storage_access_redirect.js +0 -17
  150. data/app/assets/javascripts/shopify_app/top_level.js +0 -2
  151. data/app/assets/javascripts/shopify_app/top_level_interaction.js +0 -11
  152. data/app/controllers/concerns/shopify_app/authenticated.rb +0 -19
  153. data/app/controllers/concerns/shopify_app/require_known_shop.rb +0 -48
  154. data/app/views/shopify_app/sessions/enable_cookies.html.erb +0 -70
  155. data/app/views/shopify_app/sessions/request_storage_access.html.erb +0 -68
  156. data/app/views/shopify_app/sessions/top_level_interaction.html.erb +0 -63
  157. data/app/views/shopify_app/shared/post_redirect_to_auth_shopify.html.erb +0 -13
  158. data/docs/shopify_app/script-tags.md +0 -28
  159. data/docs/shopify_app/session-repository.md +0 -88
  160. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +0 -41
  161. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +0 -62
  162. data/lib/shopify_app/controller_concerns/itp.rb +0 -45
  163. data/lib/shopify_app/jobs/scripttags_manager_job.rb +0 -16
  164. data/lib/shopify_app/managers/scripttags_manager.rb +0 -84
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ module EnsureInstalled
5
+ extend ActiveSupport::Concern
6
+ include ShopifyApp::RedirectForEmbedded
7
+
8
+ included do
9
+ if defined?(ShopifyApp::LoginProtection) && ancestors.include?(ShopifyApp::LoginProtection)
10
+ message = <<~EOS
11
+ We detected the use of incompatible concerns (EnsureInstalled and LoginProtection) in #{name},
12
+ which leads to unpredictable behavior. You cannot include both concerns in the same controller.
13
+ EOS
14
+
15
+ raise message
16
+ end
17
+
18
+ before_action :check_shop_domain
19
+
20
+ if ShopifyApp.configuration.use_new_embedded_auth_strategy?
21
+ include ShopifyApp::TokenExchange
22
+ around_action :activate_shopify_session
23
+ else
24
+ before_action :check_shop_known
25
+ before_action :validate_non_embedded_session
26
+ end
27
+ end
28
+
29
+ def current_shopify_domain
30
+ if params[:shop].blank?
31
+ ShopifyApp::Logger.info("Could not identify installed store from current_shopify_domain")
32
+ return
33
+ end
34
+
35
+ @shopify_domain ||= ShopifyApp::Utils.sanitize_shop_domain(params[:shop])
36
+ ShopifyApp::Logger.info("Installed store: #{@shopify_domain} - deduced from Shopify Admin params")
37
+ @shopify_domain
38
+ end
39
+
40
+ def installed_shop_session
41
+ @installed_shop_session ||= SessionRepository.retrieve_shop_session_by_shopify_domain(current_shopify_domain)
42
+ end
43
+
44
+ private
45
+
46
+ def check_shop_domain
47
+ redirect_to(ShopifyApp.configuration.login_url) unless current_shopify_domain
48
+ end
49
+
50
+ def check_shop_known
51
+ @shop = installed_shop_session
52
+ unless @shop
53
+ if embedded_param?
54
+ redirect_for_embedded
55
+ else
56
+ redirect_to(shop_login)
57
+ end
58
+ end
59
+ end
60
+
61
+ def shop_login
62
+ url = URI(ShopifyApp.configuration.login_url)
63
+
64
+ url.query = URI.encode_www_form(
65
+ shop: params[:shop],
66
+ host: params[:host],
67
+ return_to: request.fullpath,
68
+ )
69
+
70
+ url.to_s
71
+ end
72
+
73
+ def validate_non_embedded_session
74
+ return if loaded_directly_from_admin?
75
+
76
+ client = ShopifyAPI::Clients::Rest::Admin.new(session: installed_shop_session)
77
+ client.get(path: "shop")
78
+ rescue ShopifyAPI::Errors::HttpResponseError => error
79
+ ShopifyApp::Logger.info("Shop offline session no longer valid. Redirecting to OAuth install")
80
+ redirect_to(shop_login) if error.code == 401
81
+ raise error if error.code != 401
82
+ end
83
+ end
84
+ end
@@ -6,7 +6,11 @@ module ShopifyApp
6
6
  include ShopifyApp::RedirectForEmbedded
7
7
 
8
8
  included do
9
- before_action :login_on_scope_changes
9
+ # Embedded auth strategy uses Shopify managed install to ensure latest access scopes,
10
+ # This will be handled automatically through token exchange
11
+ unless ShopifyApp.configuration.use_new_embedded_auth_strategy?
12
+ before_action :login_on_scope_changes
13
+ end
10
14
  end
11
15
 
12
16
  protected
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShopifyApp
4
4
  class AuthenticatedController < ActionController::Base
5
- include ShopifyApp::Authenticated
5
+ include ShopifyApp::EnsureHasSession
6
6
 
7
7
  protect_from_forgery with: :exception
8
8
  end
@@ -8,49 +8,118 @@ module ShopifyApp
8
8
 
9
9
  def callback
10
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
11
+ api_session, cookie = validated_auth_objects
12
+ rescue => error
13
+ if error.class.module_parent == ShopifyAPI::Errors
14
+ callback_rescue(error)
15
+ return respond_with_error
16
+ else
17
+ raise error
18
+ end
22
19
  end
23
20
 
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
- }
21
+ save_session(api_session) if api_session
22
+ update_rails_cookie(api_session, cookie)
30
23
 
31
- session[:shopify_user_id] = auth_result[:session].associated_user.id if auth_result[:session].online?
24
+ return respond_with_user_token_flow if start_user_token_flow?(api_session)
32
25
 
33
- if start_user_token_flow?(auth_result[:session])
34
- return respond_with_user_token_flow
26
+ if ShopifyApp::VERSION < "23.0"
27
+ # deprecated in 23.0
28
+ if ShopifyApp.configuration.custom_post_authenticate_tasks.present?
29
+ ShopifyApp.configuration.post_authenticate_tasks.perform(api_session)
30
+ else
31
+ perform_post_authenticate_jobs(api_session)
32
+ end
33
+ else
34
+ ShopifyApp.configuration.post_authenticate_tasks.perform(api_session)
35
35
  end
36
+ redirect_to_app if check_billing(api_session)
37
+ end
36
38
 
37
- perform_post_authenticate_jobs(auth_result[:session])
38
- has_payment = check_billing(auth_result[:session])
39
+ private
39
40
 
40
- respond_successfully if has_payment
41
+ def callback_rescue(error)
42
+ ShopifyApp::Logger.debug("#{error.class} was rescued and redirected to login_url_with_optional_shop")
41
43
  end
42
44
 
43
- private
45
+ def deprecate_callback_rescue(error)
46
+ message = <<~EOS
47
+ An error of type #{error.class} was rescued. This is not part of `ShopifyAPI::Errors`, which could indicate a
48
+ bug in your app, or a bug in the shopify_app gem. Future versions of the gem may re-raise this error rather
49
+ than rescuing it.
50
+ EOS
51
+ ShopifyApp::Logger.deprecated(message, "22.0.0")
52
+ end
53
+
54
+ def save_session(api_session)
55
+ ShopifyApp::SessionRepository.store_session(api_session)
56
+ end
57
+
58
+ def validated_auth_objects
59
+ filtered_params = request.parameters.symbolize_keys.slice(:code, :shop, :timestamp, :state, :host, :hmac)
44
60
 
45
- def respond_successfully
61
+ oauth_payload = ShopifyAPI::Auth::Oauth.validate_auth_callback(
62
+ cookies: {
63
+ ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME =>
64
+ cookies.encrypted[ShopifyAPI::Auth::Oauth::SessionCookie::SESSION_COOKIE_NAME],
65
+ },
66
+ auth_query: ShopifyAPI::Auth::Oauth::AuthQuery.new(**filtered_params),
67
+ )
68
+ api_session = oauth_payload.dig(:session)
69
+ cookie = oauth_payload.dig(:cookie)
70
+
71
+ [api_session, cookie]
72
+ end
73
+
74
+ def update_rails_cookie(api_session, cookie)
75
+ if cookie.value.present?
76
+ cookies.encrypted[cookie.name] = {
77
+ expires: cookie.expires,
78
+ secure: true,
79
+ http_only: true,
80
+ value: cookie.value,
81
+ }
82
+ end
83
+
84
+ session[:shopify_user_id] = api_session.associated_user.id if api_session.online?
85
+ ShopifyApp::Logger.debug("Saving Shopify user ID to cookie")
86
+ end
87
+
88
+ def redirect_to_app
46
89
  if ShopifyAPI::Context.embedded?
47
- return_to = session.delete(:return_to) || ""
48
- redirect_to(ShopifyAPI::Auth.embedded_app_url(params[:host]) + return_to, allow_other_host: true)
90
+ return_to = session.delete(:return_to)
91
+ redirect_to = if fully_formed_url?(return_to)
92
+ return_to
93
+ else
94
+ "#{decoded_host}#{return_to}"
95
+ end
96
+
97
+ redirect_to = ShopifyApp.configuration.root_url if deduced_phishing_attack?
98
+ redirect_to(redirect_to, allow_other_host: true)
49
99
  else
50
100
  redirect_to(return_address)
51
101
  end
52
102
  end
53
103
 
104
+ def fully_formed_url?(return_to)
105
+ uri = Addressable::URI.parse(return_to)
106
+ uri.present? && uri.scheme.present? && uri.host.present?
107
+ end
108
+
109
+ def decoded_host
110
+ @decoded_host ||= ShopifyAPI::Auth.embedded_app_url(params[:host])
111
+ end
112
+
113
+ # host param doesn't match the configured myshopify_domain
114
+ def deduced_phishing_attack?
115
+ sanitized_host = ShopifyApp::Utils.sanitize_shop_domain(URI(decoded_host).host)
116
+ if sanitized_host.nil?
117
+ ShopifyApp::Logger.info("host param from callback is not from a trusted domain")
118
+ ShopifyApp::Logger.info("redirecting to root as this is likely a phishing attack")
119
+ end
120
+ sanitized_host.nil?
121
+ end
122
+
54
123
  def respond_with_error
55
124
  flash[:error] = I18n.t("could_not_log_in")
56
125
  redirect_to(login_url_with_optional_shop)
@@ -78,8 +147,11 @@ module ShopifyApp
78
147
  end
79
148
 
80
149
  def perform_post_authenticate_jobs(session)
81
- install_webhooks(session)
82
- install_scripttags(session)
150
+ # Ensure we use the shop session to install webhooks
151
+ session_for_shop = session.online? ? shop_session : session
152
+
153
+ install_webhooks(session_for_shop)
154
+
83
155
  perform_after_authenticate_job(session)
84
156
  end
85
157
 
@@ -89,16 +161,6 @@ module ShopifyApp
89
161
  WebhooksManager.queue(session.shop, session.access_token)
90
162
  end
91
163
 
92
- def install_scripttags(session)
93
- return unless ShopifyApp.configuration.has_scripttags?
94
-
95
- ScripttagsManager.queue(
96
- session.shop,
97
- session.access_token,
98
- ShopifyApp.configuration.scripttags
99
- )
100
- end
101
-
102
164
  def perform_after_authenticate_job(session)
103
165
  config = ShopifyApp.configuration.after_authenticate_job
104
166
 
@@ -9,7 +9,10 @@ module ShopifyApp
9
9
  private
10
10
 
11
11
  def verify_request
12
- head(:unauthorized) unless hmac_valid?(request.body.read)
12
+ unless hmac_valid?(request.body.read)
13
+ head(:unauthorized)
14
+ ShopifyApp::Logger.debug("Extension verification failed due to invalid HMAC")
15
+ end
13
16
  end
14
17
  end
15
18
  end
@@ -7,7 +7,7 @@ module ShopifyApp
7
7
 
8
8
  layout false, only: :new
9
9
 
10
- after_action only: [:new, :create] do |controller|
10
+ after_action only: [:new, :create, :patch_shopify_id_token] do |controller|
11
11
  controller.response.headers.except!("X-Frame-Options")
12
12
  end
13
13
 
@@ -19,6 +19,10 @@ module ShopifyApp
19
19
  authenticate
20
20
  end
21
21
 
22
+ def patch_shopify_id_token
23
+ render(layout: "shopify_app/layouts/app_bridge")
24
+ end
25
+
22
26
  def top_level_interaction
23
27
  @url = login_url_with_optional_shop(top_level: true)
24
28
  validate_shop_presence
@@ -27,6 +31,8 @@ module ShopifyApp
27
31
  def destroy
28
32
  reset_session
29
33
  flash[:notice] = I18n.t(".logged_out")
34
+ ShopifyApp::Logger.debug("Session destroyed")
35
+ ShopifyApp::Logger.debug("Redirecting to #{login_url_with_optional_shop}")
30
36
  redirect_to(login_url_with_optional_shop)
31
37
  end
32
38
 
@@ -35,28 +41,47 @@ module ShopifyApp
35
41
  def authenticate
36
42
  return render_invalid_shop_error unless sanitized_shop_name.present?
37
43
 
44
+ if ShopifyApp.configuration.use_new_embedded_auth_strategy?
45
+ ShopifyApp::Logger.debug("Starting OAuth - Redirecting to Shopify managed install")
46
+ start_install
47
+ else
48
+ ShopifyApp::Logger.debug("Starting OAuth - Redirecting to begin auth")
49
+ start_oauth
50
+ end
51
+ end
52
+
53
+ def start_install
54
+ shop_name = sanitized_shop_name.split(".").first
55
+ unified_admin_path = ShopifyApp::Utils.unified_admin_path(shop_name)
56
+ install_path = "#{unified_admin_path}/oauth/install?client_id=#{ShopifyApp.configuration.api_key}"
57
+ redirect_to(install_path, allow_other_host: true)
58
+ end
59
+
60
+ def start_oauth
38
61
  copy_return_to_param_to_session
39
62
 
40
63
  if embedded_redirect_url?
64
+ ShopifyApp::Logger.debug("Embedded URL within / authenticate")
41
65
  if embedded_param?
42
66
  redirect_for_embedded
43
67
  else
44
- start_oauth
68
+ redirect_to_begin_oauth
45
69
  end
46
70
  elsif top_level?
47
- start_oauth
71
+ redirect_to_begin_oauth
48
72
  else
49
73
  redirect_auth_to_top_level
50
74
  end
51
75
  end
52
76
 
53
- def start_oauth
77
+ def redirect_to_begin_oauth
54
78
  callback_url = ShopifyApp.configuration.login_callback_url.gsub(%r{^/}, "")
79
+ ShopifyApp::Logger.debug("Starting OAuth with the following callback URL: #{callback_url}")
55
80
 
56
81
  auth_attributes = ShopifyAPI::Auth::Oauth.begin_auth(
57
82
  shop: sanitized_shop_name,
58
83
  redirect_path: "/#{callback_url}",
59
- is_online: user_session_expected?
84
+ is_online: user_session_expected?,
60
85
  )
61
86
  cookies.encrypted[auth_attributes[:cookie].name] = {
62
87
  expires: auth_attributes[:cookie].expires,
@@ -65,7 +90,10 @@ module ShopifyApp
65
90
  value: auth_attributes[:cookie].value,
66
91
  }
67
92
 
68
- redirect_to(auth_attributes[:auth_route], allow_other_host: true)
93
+ auth_route = auth_attributes[:auth_route]
94
+
95
+ ShopifyApp::Logger.debug("Redirecting to auth_route - #{auth_route}")
96
+ redirect_to(auth_route, allow_other_host: true)
69
97
  end
70
98
 
71
99
  def validate_shop_presence
@@ -94,7 +122,9 @@ module ShopifyApp
94
122
  end
95
123
 
96
124
  def redirect_auth_to_top_level
97
- fullpage_redirect_to(login_url_with_optional_shop(top_level: true))
125
+ url = login_url_with_optional_shop(top_level: true)
126
+ ShopifyApp::Logger.debug("Redirecting to top level - #{url}")
127
+ fullpage_redirect_to(url)
98
128
  end
99
129
  end
100
130
  end
@@ -8,7 +8,7 @@ module ShopifyApp
8
8
  params.permit!
9
9
 
10
10
  ShopifyAPI::Webhooks::Registry.process(
11
- ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
11
+ ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h),
12
12
  )
13
13
  head(:ok)
14
14
  end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title><%= ShopifyApp.configuration.application_name %></title>
6
+ <%= yield :head %>
7
+ <script
8
+ data-api-key="<%= ShopifyApp.configuration.api_key %>"
9
+ src="https://cdn.shopify.com/shopifycloud/app-bridge.js">
10
+ </script>
11
+ <%= csrf_meta_tags %>
12
+ </head>
13
+
14
+ <body>
15
+ <%= yield %>
16
+ </body>
17
+ </html>
@@ -5,9 +5,18 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <base target="_top">
7
7
  <title>Redirecting…</title>
8
+
9
+ <%
10
+ is_iframe = local_assigns.key?(:is_iframe) ? is_iframe : true
11
+ if is_iframe
12
+ %>
13
+ <meta name="shopify-api-key" content="<%= ShopifyApp.configuration.api_key %>">
14
+ <%= javascript_include_tag "https://cdn.shopify.com/shopifycloud/app-bridge.js" %>
15
+ <% end %>
16
+
8
17
  <%= javascript_include_tag('shopify_app/redirect', crossorigin: 'anonymous', integrity: true) %>
9
18
  </head>
10
- <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="<%= current_shopify_domain %>" data-host="<%= params[:host] %>" >
19
+ <body>
11
20
  <%=
12
21
  content_tag(:div, nil,
13
22
  id: 'redirection-target',
@@ -3,21 +3,3 @@ cs:
3
3
  logged_out: Odhlášení proběhlo úspěšně
4
4
  could_not_log_in: Nelze se přihlásit do obchodu Shopify
5
5
  invalid_shop_url: Neplatná doména obchodu
6
- enable_cookies_heading: Zapnout soubory cookie z aplikace %{app}
7
- enable_cookies_body: Pokud chcete v Shopify používat aplikaci %{app}, musíte soubory
8
- cookie v tomto prohlížeči povolit ručně.
9
- enable_cookies_footer: Soubory cookie umožňují, aby vás aplikace ověřila pomocí
10
- dočasného uchování preferencí a osobních údajů. Jejich platnost vyprší po 30 dnech.
11
- enable_cookies_action: Povolit soubory cookie
12
- top_level_interaction_heading: Váš prohlížeč potřebuje ověřit aplikaci %{app}
13
- top_level_interaction_body: Váš prohlížeč vyžaduje, aby si od vás aplikace, jako
14
- je %{app}, nejdřív vyžádaly přístup k souborům cookie, než je pro vás Shopify
15
- otevře.
16
- top_level_interaction_action: Pokračovat
17
- request_storage_access_heading: Aplikace %{app} potřebuje získat přístup k souborům
18
- cookie
19
- request_storage_access_body: Tato aplikace vám umožní ověření prostřednictvím dočasného
20
- uchování vašich osobních údajů. Pokud chcete používat tuto aplikaci, klikněte
21
- na tlačítko Pokračovat a povolte soubory cookie.
22
- request_storage_access_footer: Platnost souborů cookie vyprší po 30 dnech.
23
- request_storage_access_action: Pokračovat
@@ -3,18 +3,3 @@ da:
3
3
  logged_out: Logget ud
4
4
  could_not_log_in: Kunne ikke logge ind på Shopify-butik
5
5
  invalid_shop_url: Ugyldig butiksdomæne
6
- enable_cookies_heading: Aktivér cookies fra %{app}
7
- enable_cookies_body: Du skal manuelt aktivere cookies i denne browser for at kunne
8
- bruge %{app} i Shopify.
9
- enable_cookies_footer: Cookies lader appen godkende dig ved at gemme dine præferencer
10
- og personlige oplysninger midlertidigt. De udløber efter 30 dage.
11
- enable_cookies_action: Aktivér cookies
12
- top_level_interaction_heading: Din browser skal godkende %{app}
13
- top_level_interaction_body: Din browser kræver, at apps som f.eks. %{app} spørger
14
- dig om adgang til cookies, inden Shopify kan åbne den for dig.
15
- top_level_interaction_action: Fortsæt
16
- request_storage_access_heading: "%{app} skal have adgang til cookies"
17
- request_storage_access_body: Det lader appen godkende dig ved at gemme dine personlige
18
- oplysninger midlertidigt. Klik på forsæt, og tillad cookies for at bruge appen.
19
- request_storage_access_footer: Cookies udløber efter 30 dage.
20
- request_storage_access_action: Fortsæt
@@ -3,20 +3,3 @@ de:
3
3
  logged_out: Erfolgreich ausgelogt
4
4
  could_not_log_in: Shopify Store Login fehlgeschlagen
5
5
  invalid_shop_url: Ungültige Shop-Domain
6
- enable_cookies_heading: Cookies von %{app} aktivieren
7
- enable_cookies_body: Du musst Cookies in diesem Browser manuell aktivieren, um %{app}
8
- in Shopify verwenden zu können.
9
- enable_cookies_footer: Mithilfe von Cookies kann die App dich authentifizieren,
10
- indem deine Einstellungen und personenbezogenen Daten vorübergehend gespeichert
11
- werden. Sie laufen nach 30 Tagen ab.
12
- enable_cookies_action: Cookies aktivieren
13
- top_level_interaction_heading: Dein Browser muss %{app} authentifizieren
14
- top_level_interaction_body: Dein Browser verlangt, dass Apps wie %{app} dich um
15
- Zugriff auf Cookies bitten, bevor Shopify sie für dich öffnen kann.
16
- top_level_interaction_action: Weiter
17
- request_storage_access_heading: "%{app} braucht Zugriff auf Cookies"
18
- request_storage_access_body: Damit kann die App dich authentifizieren, indem deine
19
- Einstellungen und personenbezogenen Daten vorübergehend gespeichert werden. Klicke
20
- auf "Weiter" und erlaube Cookies, um die App zu verwenden.
21
- request_storage_access_footer: Cookies laufen nach 30 Tagen ab.
22
- request_storage_access_action: Weiter
@@ -2,14 +2,3 @@ en:
2
2
  logged_out: 'Successfully logged out'
3
3
  could_not_log_in: 'Could not log in to Shopify store'
4
4
  invalid_shop_url: 'Invalid shop domain'
5
- enable_cookies_heading: "Enable cookies from %{app}"
6
- enable_cookies_body: "You must manually enable cookies in this browser in order to use %{app} within Shopify."
7
- enable_cookies_footer: 'Cookies let the app authenticate you by temporarily storing your preferences and personal information. They expire after 30 days.'
8
- enable_cookies_action: 'Enable cookies'
9
- top_level_interaction_heading: "Your browser needs to authenticate %{app}"
10
- top_level_interaction_body: "Your browser requires apps like %{app} to ask you for access to cookies before Shopify can open it for you."
11
- top_level_interaction_action: 'Continue'
12
- request_storage_access_heading: "%{app} needs access to cookies"
13
- request_storage_access_body: "This lets the app authenticate you by temporarily storing your personal information. Click continue and allow cookies to use the app."
14
- request_storage_access_footer: 'Cookies expire after 30 days.'
15
- request_storage_access_action: 'Continue'
@@ -3,20 +3,3 @@ es:
3
3
  logged_out: Cerrar sesión
4
4
  could_not_log_in: No se pudo iniciar sesión en tu tienda Shopify
5
5
  invalid_shop_url: Dominio de tienda inválido
6
- enable_cookies_heading: Habilitar cookies de %{app}
7
- enable_cookies_body: Debes habilitar manualmente las cookies en este navegador para
8
- usar %{app} en Shopify.
9
- enable_cookies_footer: Las cookies permiten que la aplicación te autentique almacenando
10
- temporalmente tus preferencias y datos personales. Las cookies expiran al cabo
11
- de 30 días.
12
- enable_cookies_action: Habilitar cookies
13
- top_level_interaction_heading: Tu navegador necesita autenticar %{app}
14
- top_level_interaction_body: Tu navegador requiere aplicaciones como %{app} para
15
- solicitarte acceso a cookies antes de que Shopify pueda abrirlo por ti.
16
- top_level_interaction_action: Continuar
17
- request_storage_access_heading: "%{app} necesita acceso a las cookies"
18
- request_storage_access_body: Esto permite que la aplicación te autentique almacenando
19
- temporalmente tus datos personales. Haz clic en continuar y permite que las cookies
20
- utilicen la aplicación.
21
- request_storage_access_footer: Las cookies expiran a los 30 días.
22
- request_storage_access_action: Continuar
@@ -3,18 +3,3 @@ fi:
3
3
  logged_out: Olet kirjautunut ulos
4
4
  could_not_log_in: Kirjautuminen Shopify-kauppaan ei onnistunut
5
5
  invalid_shop_url: Virheellinen kaupan verkkotunnus
6
- enable_cookies_heading: Ota käyttöön sovelluksen %{app} evästeet
7
- enable_cookies_body: Sinun on otettava evästeet käyttöön manuaalisesti tässä selaimessa,
8
- jotta voit käyttää sovellusta %{app} Shopifyssa.
9
- enable_cookies_footer: Evästeiden avulla sovellus voi todentaa sinut tallentamalla
10
- asetuksesi ja henkilötietosi tilapäisesti. Ne vanhenevat 30 päivän kuluttua.
11
- enable_cookies_action: Ota evästeet käyttöön
12
- top_level_interaction_heading: Selaimesi täytyy todentaa %{app}
13
- top_level_interaction_body: Selaimesi vaatii sovelluksia, kuten %{app}, pyytämään
14
- sinulta luvan evästeiden käyttöön, ennen kuin Shopify voi avata sovelluksen.
15
- top_level_interaction_action: Jatka
16
- request_storage_access_heading: "%{app} edellyttää evästeiden käyttöä"
17
- request_storage_access_body: Näin sovellus voi todentaa sinut tallentamalla henkilötietosi
18
- tilapäisesti. Klikkaa Jatka ja salli evästeet sovelluksen käyttämiseksi.
19
- request_storage_access_footer: Evästeet vanhenevat 30 päivän kuluttua.
20
- request_storage_access_action: Jatka
@@ -3,21 +3,3 @@ fr:
3
3
  logged_out: Vous êtes déconnecté(e)
4
4
  could_not_log_in: Impossible de se connecter à la boutique Shopify
5
5
  invalid_shop_url: Url invalide
6
- enable_cookies_heading: Activer les cookies de %{app}
7
- enable_cookies_body: Vous devez manuellement activer les cookies dans ce navigateur
8
- pour utiliser %{app} dans Shopify.
9
- enable_cookies_footer: Les cookies permettent à l'application de vous authentifier
10
- en stockant temporairement vos préférences et informations personnelles. Celles-ci
11
- expirent après 30 jours.
12
- enable_cookies_action: Activer les cookies
13
- top_level_interaction_heading: Votre navigateur doit s'authentifier %{app}
14
- top_level_interaction_body: Votre navigateur nécessite des applications telles que
15
- %{app} pour vous demander l'accès aux cookies avant que Shopify ne puisse l'ouvrir
16
- pour vous.
17
- top_level_interaction_action: Continuer
18
- request_storage_access_heading: "%{app} a besoin d'accéder aux cookies"
19
- request_storage_access_body: Cela permet à l'application de vous authentifier en
20
- stockant temporairement vos informations personnelles. Cliquez pour continuer
21
- et autorisez les cookies à utiliser l'application.
22
- request_storage_access_footer: Les cookies expirent après 30 jours.
23
- request_storage_access_action: Continuer
@@ -3,19 +3,3 @@ it:
3
3
  logged_out: Disconnessione effettuata correttamente
4
4
  could_not_log_in: Impossibile accedere al negozio Shopify
5
5
  invalid_shop_url: Dominio negozio non valido
6
- enable_cookies_heading: Abilita i cookie di %{app}
7
- enable_cookies_body: Devi abilitare manualmente i cookie in questo browser per poter
8
- utilizzare %{app} da Shopify.
9
- enable_cookies_footer: I cookie consentono all'app di autenticarti memorizzando
10
- temporaneamente le tue preferenze e informazioni personali. Scadono dopo 30 giorni.
11
- enable_cookies_action: Abilita i cookie
12
- top_level_interaction_heading: Il tuo browser deve autenticare %{app}
13
- top_level_interaction_body: Il tuo browser richiede che app come %{app} ti chiedano
14
- l'accesso ai cookie prima dell'apertura automatica da parte di Shopify.
15
- top_level_interaction_action: Continua
16
- request_storage_access_heading: "%{app} deve accedere ai cookie"
17
- request_storage_access_body: L'app potrà così autenticarti memorizzando temporaneamente
18
- le tue informazioni personali. Clicca su Continua e consenti ai cookie di utilizzare
19
- l'app.
20
- request_storage_access_footer: I cookie scadono dopo 30 giorni.
21
- request_storage_access_action: Continua
@@ -3,15 +3,3 @@ ja:
3
3
  logged_out: ログアウトに成功しました
4
4
  could_not_log_in: Shopifyストアにログインできませんでした
5
5
  invalid_shop_url: ショップのドメインが無効です
6
- enable_cookies_heading: "%{app}からのCookieを有効にする"
7
- enable_cookies_body: Shopifyで%{app}を使用できるようにするためには、このブラウザのCookieを手動で有効にする必要があります。
8
- enable_cookies_footer: Cookieを使用すると、各種設定や個人情報を一時的に保存することで、アプリ認証を受けることができます。30日後に有効期限が切れます。
9
- enable_cookies_action: Cookieを有効にする
10
- top_level_interaction_heading: お使いのブラウザを更新する必要があります%{app}
11
- top_level_interaction_body: Shopifyがアプリを開けるように、ブラウザーはCookieにアクセスするための%{app}のようなアプリが必要です。
12
- top_level_interaction_action: 続ける
13
- request_storage_access_heading: "%{app}はCookieへのアクセス許可が必要です"
14
- request_storage_access_body: Cookieを使用すると、個人情報を一時的に保存することで、アプリ認証を受けることができます。[続ける]
15
- をクリックすると、アプリはCookieを利用します。
16
- request_storage_access_footer: Cookieは30日後に有効期限が切れます。
17
- request_storage_access_action: 続ける
@@ -3,17 +3,3 @@ ko:
3
3
  logged_out: 성공적으로 로그아웃 되었습니다.
4
4
  could_not_log_in: Shopify 스토어에 로그인할 수 없습니다.
5
5
  invalid_shop_url: 유효하지 않은 상점 도메인
6
- enable_cookies_heading: "%{app}에서 쿠키를 사용 가능"
7
- enable_cookies_body: Shopify 내에서 %{app} 을 사용하기 위해 이 브라우저에서 쿠키를 수동으로 사용할 수 있습니다.
8
- enable_cookies_footer: 쿠키를 사용하면 개인의 선호나 정보를 임시로 저장하여 앱에서 사용자를 인증할 수 있습니다. 쿠키는 30일
9
- 후에 만료됩니다.
10
- enable_cookies_action: 쿠리 사용 가능
11
- top_level_interaction_heading: 브라우저에서 %{app} 을 인증해야 합니다.
12
- top_level_interaction_body: Shopify를 열기 전 쿠키에 엑세스하려면 %{app} 과 같은 앱 들이 브라우저에 설치되어야
13
- 합니다.
14
- top_level_interaction_action: 계속
15
- request_storage_access_heading: "%{app}에서 쿠키에 접근해야 합니다."
16
- request_storage_access_body: 이를 통해 개인 정보를 임시로 저장하여 앱에서 사용자를 인증할 수 있습니다. 계속 클릭하여
17
- 쿠키로 앱을 사용하세요.
18
- request_storage_access_footer: 쿠키는 30일 후에 만료됩니다.
19
- request_storage_access_action: 계속