shopify_app 21.3.1 → 21.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/ENHANCEMENT.md +9 -0
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +24 -46
  4. data/.github/ISSUE_TEMPLATE/feature-request.md +5 -29
  5. data/CHANGELOG.md +9 -0
  6. data/Gemfile.lock +11 -13
  7. data/app/controllers/concerns/shopify_app/ensure_installed.rb +18 -2
  8. data/app/controllers/shopify_app/callback_controller.rb +4 -0
  9. data/docs/shopify_app/engine.md +2 -11
  10. data/docs/shopify_app/session-repository.md +1 -10
  11. data/docs/shopify_app/testing.md +32 -10
  12. data/lib/shopify_app/controller_concerns/login_protection.rb +3 -1
  13. data/lib/shopify_app/controller_concerns/redirect_for_embedded.rb +5 -1
  14. data/lib/shopify_app/managers/webhooks_manager.rb +3 -3
  15. data/lib/shopify_app/session/jwt.rb +1 -2
  16. data/lib/shopify_app/test_helpers/all.rb +1 -0
  17. data/lib/shopify_app/test_helpers/shopify_session_helper.rb +15 -0
  18. data/lib/shopify_app/utils.rb +59 -28
  19. data/lib/shopify_app/version.rb +1 -1
  20. data/lib/shopify_app.rb +1 -0
  21. data/package.json +1 -1
  22. data/shopify_app.gemspec +2 -1
  23. metadata +20 -17
  24. data/app/assets/javascripts/shopify_app/enable_cookies.js +0 -3
  25. data/app/assets/javascripts/shopify_app/itp_helper.js +0 -40
  26. data/app/assets/javascripts/shopify_app/partition_cookies.js +0 -8
  27. data/app/assets/javascripts/shopify_app/post_redirect.js +0 -9
  28. data/app/assets/javascripts/shopify_app/request_storage_access.js +0 -3
  29. data/app/assets/javascripts/shopify_app/storage_access.js +0 -148
  30. data/app/assets/javascripts/shopify_app/storage_access_redirect.js +0 -17
  31. data/app/assets/javascripts/shopify_app/top_level.js +0 -2
  32. data/app/assets/javascripts/shopify_app/top_level_interaction.js +0 -11
  33. data/app/views/shopify_app/sessions/enable_cookies.html.erb +0 -70
  34. data/app/views/shopify_app/sessions/request_storage_access.html.erb +0 -68
  35. data/app/views/shopify_app/sessions/top_level_interaction.html.erb +0 -63
  36. data/app/views/shopify_app/shared/post_redirect_to_auth_shopify.html.erb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00bf2ba9c43324a703b21367459c592b93d092f2926f2d6862bfb7efb9494853
4
- data.tar.gz: 7792d5fd3f1a5af452357c0c6f26b8c245e471171e86a95dad5559bdb87ab6d8
3
+ metadata.gz: f8bffcbbd9262af14d8b5d0c0333b2a17f4de563ad4c40134dec7b838eaf789b
4
+ data.tar.gz: 0ba89c4f9ff64b366c3d368b70abcfdf2e02f1e0f66c796f5b16a6a5fafdcd07
5
5
  SHA512:
6
- metadata.gz: f963fc4af5645f47d072aff3d0aa9cf47b7f1c20d422fcf43ec4466804d24a18916864d96372c407c1ef7c2e8963a3b5946763534bde5070ebd767ce1da3957d
7
- data.tar.gz: 79cd7bd816b1b9759d6531f9c9a65fe81d6d7c9ed1e671c127c44b2f74a2b962d27d0eb5ac9b8c48378f012aec88451d9c533bfca345d14a6c8f0193993378a4
6
+ metadata.gz: bc4dc9ccd6267a2eb7ff1d22e54f1527c0ab32cb61b83841bc71cc4bb79b14a33aa4021e13494e3167d0a7d20aeea86f9af9dd478ebff8400ec4b33ac1426417
7
+ data.tar.gz: c2154db900a3fb4321be1d224c8d44987fdce714984b3004b87b4dd5301717832fa11b907fe88b261020da82b4c3255256d4e4ba4dd5800262a8e3daa4778f91
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: '📈 Enhancement'
3
+ about: Enhancement to our codebase that isn't a adding or changing a feature
4
+ labels: 'Type: Enhancement 📈'
5
+ ---
6
+
7
+ ## Overview/summary
8
+
9
+ <!-- Write a short description of the enhancement here ↓ -->
@@ -1,63 +1,41 @@
1
1
  ---
2
- name: Bug report
3
- about: Report a technical issue with the Shopify App gem.
4
- labels: bug
2
+ name: "🐛 Bug Report"
3
+ about: Something isn't working
4
+ labels: "Type: Bug 🐛"
5
5
  ---
6
6
 
7
- <!--
7
+ # Issue summary
8
8
 
9
- Do you want to ask a question? Are you looking for support? The Shopify Community forum is the best place for getting support: https://community.shopify.com
9
+ <!--
10
10
 
11
- You can also join the Partners Slack Community group: https://www.shopify.com/partners/community#conversation
11
+ Write a short description of the issue here. Please provide any details or logs that
12
+ can help us debug it.
12
13
 
13
- Authentication Issues: A great deal of the issues surrounding this repo are around authenticating (installing) the generated app with Shopify.
14
+ Increase the logs as described in the README by setting log_level to :debug, and paste the relevant portion here.
14
15
 
15
- If you are experiencing issues with your app authenticating/installing the best way to get help fast is to create a repo with the minimal amount of code to demonstrate the issue and a clearly documented set of steps you took to arrive there. This will help us solve your problem quicker since we won't need to spend any time figuring out how to reproduce the bug. Please also include your operating system and browser.
16
+ Learn more: https://github.com/Shopify/shopify-api-ruby#setup-shopify-context
16
17
 
17
18
  -->
18
19
 
19
- ### Description
20
-
21
- <!-- Description of the issue -->
22
-
23
- ### Steps to Reproduce
24
-
25
- 1. <!-- First Step -->
26
- 2. <!-- Second Step -->
27
- 3. <!-- and so on… -->
28
-
29
- **Expected behavior:**
30
-
31
- <!-- What you expect to happen -->
32
-
33
- **Actual behavior:**
34
-
35
- <!-- What actually happens -->
36
-
37
- **Reproduces how often:**
38
-
39
- <!-- What percentage of the time does it reproduce? -->
40
-
41
- ### Browsers
42
-
43
- <!-- Please specify the browser(s) you have tested that exhibit this behaviour. -->
44
-
45
- ### Gem versions
46
-
47
- <!-- Please specify which version(s) of the gem exhibit this behaviour. -->
48
-
49
- ### Additional Information
20
+ - `shopify_api` version:
21
+ - `shopify_app` version:
22
+ - Ruby version:
23
+ - Operating system:
50
24
 
51
- <!-- Any additional information, configuration or data that might be necessary to reproduce the issue. See common examples of important information below. -->
25
+ ```
26
+ // Paste any relevant logs here
27
+ ```
52
28
 
53
- <!-- - [x] My app relies on third-party cookies -->
54
- <!-- - [x] My app is intended to be a non-embedded app -->
55
- <!-- - [x] My app uses session tokens -->
29
+ ## Expected behavior
56
30
 
31
+ <!-- What do you think should happen? -->
57
32
 
58
- ### Security
33
+ ## Actual behavior
59
34
 
60
- <!-- Please be certain to redact any private information from your logs or code snippets such as Api Keys, Api Secrets, and any authentication tokens such as shop_tokens. -->
35
+ <!-- What actually happens? -->
61
36
 
62
- - [ ] I have redacted any private information from my logs or code snippets.
37
+ ## Steps to reproduce the problem
63
38
 
39
+ 1.
40
+ 1.
41
+ 1.
@@ -1,33 +1,9 @@
1
1
  ---
2
- name: Feature request
3
- about: Request new functionality for the Shopify App gem.
4
- labels: feature request
2
+ name: "🙌 Feature Request"
3
+ about: Suggest a new feature, or changes to an existing one
4
+ labels: "Type: Feature Request :raised_hands:"
5
5
  ---
6
6
 
7
- <!--
7
+ ## Overview
8
8
 
9
- Do you want to ask a question? Are you looking for support? The Shopify Community forum is the best place for getting support: https://community.shopify.com
10
-
11
- You can also join the Partners Slack Community group: https://www.shopify.com/partners/community#conversation
12
-
13
- ---
14
-
15
- Please note that the team that maintains this gem has finite resources so it's unlikely that we'll work on feature requests. If we're interested in a particular feature however, we'll follow up and ask for more detail.
16
-
17
- -->
18
-
19
- ### Summary
20
-
21
- <!-- One paragraph explanation of the feature or suggestions. -->
22
-
23
- ### Motivation
24
-
25
- <!-- Why is this feature or suggestion needed? What is the expected outcome? -->
26
-
27
- ### Describe alternatives you've considered
28
-
29
- <!-- A clear and concise description of the alternative solutions you've considered. -->
30
-
31
- ### Additional context
32
-
33
- <!-- Add any other context or screenshots about the feature request here. -->
9
+ <!-- Write a short description of the request here -->
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  Unreleased
2
2
  ----------
3
3
 
4
+ 21.4.0 (Jan 5, 2023)
5
+ ----------
6
+ * Updated shopify_api to 12.4.0 [#1633](https://github.com/Shopify/shopify_app/pull/1633)
7
+ * Removed Logged output for rescued JWT exceptions [#1610](https://github.com/Shopify/shopify_app/pull/1610)
8
+ * Fixes a bug with `ShopifyApp::WebhooksManager.destroy_webhooks` causing not passing session arguments to [unregister](https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/webhooks/registry.rb#L99) method [#1569](https://github.com/Shopify/shopify_app/pull/1569)
9
+ * Validates shop's offline session token is still valid when using `EnsureInstalled`[#1612](https://github.com/Shopify/shopify_app/pull/1612)
10
+ * Allows use of multiple subdomains with myshopify_domain [#1620](https://github.com/Shopify/shopify_app/pull/1620)
11
+ * Added a `setup_shopify_session` test helper to stub a valid session
12
+
4
13
  21.3.1 (Dec 12, 2022)
5
14
  ----------
6
15
  * Fix bug with stores using the new unified admin that were falsely being flagged as phishing attempts [#1608](https://github.com/Shopify/shopify_app/pull/1608)
data/Gemfile.lock CHANGED
@@ -1,13 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify_app (21.3.1)
4
+ shopify_app (21.4.0)
5
5
  activeresource
6
+ addressable (~> 2.7)
6
7
  browser_sniffer (~> 2.0)
7
8
  jwt (>= 2.2.3)
8
9
  rails (> 5.2.1)
9
10
  redirect_safely (~> 1.0)
10
- shopify_api (~> 12.3)
11
+ shopify_api (~> 12.4)
11
12
  sprockets-rails (>= 2.0.0)
12
13
 
13
14
  GEM
@@ -99,13 +100,13 @@ GEM
99
100
  activesupport (>= 5.0)
100
101
  hash_diff (1.1.1)
101
102
  hashdiff (1.0.1)
102
- httparty (0.20.0)
103
- mime-types (~> 3.0)
103
+ httparty (0.21.0)
104
+ mini_mime (>= 1.0.0)
104
105
  multi_xml (>= 0.5.2)
105
106
  i18n (1.12.0)
106
107
  concurrent-ruby (~> 1.0)
107
108
  json (2.6.3)
108
- jwt (2.5.0)
109
+ jwt (2.6.0)
109
110
  language_server-protocol (3.17.0.2)
110
111
  loofah (2.19.0)
111
112
  crass (~> 1.0.2)
@@ -117,11 +118,8 @@ GEM
117
118
  net-smtp
118
119
  marcel (1.0.2)
119
120
  method_source (1.0.0)
120
- mime-types (3.4.1)
121
- mime-types-data (~> 3.2015)
122
- mime-types-data (3.2022.0105)
123
121
  mini_mime (1.1.2)
124
- minitest (5.16.3)
122
+ minitest (5.17.0)
125
123
  mocha (2.0.2)
126
124
  ruby2_keywords (>= 0.0.5)
127
125
  multi_xml (0.6.0)
@@ -141,7 +139,7 @@ GEM
141
139
  nokogiri (1.13.9-x86_64-linux)
142
140
  racc (~> 1.4)
143
141
  oj (3.13.23)
144
- openssl (3.0.1)
142
+ openssl (3.1.0)
145
143
  parallel (1.22.1)
146
144
  parser (3.1.3.0)
147
145
  ast (~> 2.4.1)
@@ -216,8 +214,8 @@ GEM
216
214
  syntax_tree (>= 4.0.2, < 5.0.0)
217
215
  ruby-progressbar (1.11.0)
218
216
  ruby2_keywords (0.0.5)
219
- securerandom (0.2.1)
220
- shopify_api (12.3.0)
217
+ securerandom (0.2.2)
218
+ shopify_api (12.4.0)
221
219
  activesupport
222
220
  concurrent-ruby
223
221
  hash_diff
@@ -228,7 +226,7 @@ GEM
228
226
  securerandom
229
227
  sorbet-runtime
230
228
  zeitwerk (~> 2.5, < 2.6.5)
231
- sorbet-runtime (0.5.10576)
229
+ sorbet-runtime (0.5.10601)
232
230
  sprockets (4.1.1)
233
231
  concurrent-ruby (~> 1.0)
234
232
  rack (> 1, < 3)
@@ -6,7 +6,7 @@ module ShopifyApp
6
6
  include ShopifyApp::RedirectForEmbedded
7
7
 
8
8
  included do
9
- if ancestors.include?(ShopifyApp::LoginProtection)
9
+ if defined?(ShopifyApp::LoginProtection) && ancestors.include?(ShopifyApp::LoginProtection)
10
10
  message = <<~EOS
11
11
  We detected the use of incompatible concerns (EnsureInstalled and LoginProtection) in #{name},
12
12
  which may lead to unpredictable behavior. In a future release of this library this will raise an error.
@@ -17,6 +17,7 @@ module ShopifyApp
17
17
 
18
18
  before_action :check_shop_domain
19
19
  before_action :check_shop_known
20
+ before_action :validate_non_embedded_session
20
21
  end
21
22
 
22
23
  def current_shopify_domain
@@ -30,6 +31,10 @@ module ShopifyApp
30
31
  @shopify_domain
31
32
  end
32
33
 
34
+ def installed_shop_session
35
+ @installed_shop_session ||= SessionRepository.retrieve_shop_session_by_shopify_domain(current_shopify_domain)
36
+ end
37
+
33
38
  private
34
39
 
35
40
  def check_shop_domain
@@ -37,7 +42,7 @@ module ShopifyApp
37
42
  end
38
43
 
39
44
  def check_shop_known
40
- @shop = SessionRepository.retrieve_shop_session_by_shopify_domain(current_shopify_domain)
45
+ @shop = installed_shop_session
41
46
  unless @shop
42
47
  if embedded_param?
43
48
  redirect_for_embedded
@@ -58,5 +63,16 @@ module ShopifyApp
58
63
 
59
64
  url.to_s
60
65
  end
66
+
67
+ def validate_non_embedded_session
68
+ return if loaded_directly_from_admin?
69
+
70
+ client = ShopifyAPI::Clients::Rest::Admin.new(session: installed_shop_session)
71
+ client.get(path: "shop")
72
+ rescue ShopifyAPI::Errors::HttpResponseError => error
73
+ ShopifyApp::Logger.info("Shop offline session no longer valid. Redirecting to OAuth install")
74
+ redirect_to(shop_login) if error.code == 401
75
+ raise error if error.code != 401
76
+ end
61
77
  end
62
78
  end
@@ -85,6 +85,10 @@ module ShopifyApp
85
85
  # host param doesn't match the configured myshopify_domain
86
86
  def deduced_phishing_attack?
87
87
  sanitized_host = ShopifyApp::Utils.sanitize_shop_domain(URI(decoded_host).host)
88
+ if sanitized_host.nil?
89
+ ShopifyApp::Logger.info("host param from callback is not from a trusted domain")
90
+ ShopifyApp::Logger.info("redirecting to root as this is likely a phishing attack")
91
+ end
88
92
  sanitized_host.nil?
89
93
  end
90
94
 
@@ -27,24 +27,15 @@ The engine may also be mounted at a nested route, for example:
27
27
  mount ShopifyApp::Engine, at: '/nested'
28
28
  ```
29
29
 
30
- This will create the Shopify engine routes under the specified subpath. You'll also need to make some updates to your `shopify_app.rb` and `omniauth.rb` initializers. First, update the shopify_app initializer to include a custom `root_url` e.g.:
30
+ This will create the Shopify engine routes under the specified subpath. You'll also need to make some updates to your `shopify_app.rb`. Update the shopify_app initializer to include a custom `root_url` and `login_callback_url` e.g.:
31
31
 
32
32
  ```ruby
33
33
  ShopifyApp.configure do |config|
34
34
  config.root_url = '/nested'
35
+ config.login_callback_url = '/nested/auth/shopify/callback'
35
36
  end
36
37
  ```
37
38
 
38
- then update the omniauth initializer to include a custom `callback_path` e.g.:
39
-
40
- ```ruby
41
- provider :shopify,
42
- ShopifyApp.configuration.api_key,
43
- ShopifyApp.configuration.secret,
44
- scope: ShopifyApp.configuration.scope,
45
- callback_path: '/nested/auth/shopify/callback'
46
- ```
47
-
48
39
  You may also need to change your `config/routes.rb` to render a view for `/nested`, since this is what will be rendered in the Shopify Admin of any shops that have installed your app. The engine itself doesn't have a view for this, so you'll need something like this:
49
40
 
50
41
  ```ruby
@@ -72,17 +72,8 @@ end
72
72
 
73
73
  1. Run the `user_model` generator as mentioned above.
74
74
  2. Ensure that both your `Shop` model and `User` model includes the necessary concerns `ShopifyApp::ShopSessionStorage` and `ShopifyApp::UserSessionStorage`.
75
- 3. Make changes to 2 initializer files as shown below:
75
+ 3. Make changes to the `shopify_app.rb` initializer file as shown below:
76
76
  ```ruby
77
- # In the `omniauth.rb` initializer:
78
- provider :shopify,
79
- ...
80
- setup: lambda { |env|
81
- configuration = ShopifyApp::OmniAuthConfiguration.new(env['omniauth.strategy'], Rack::Request.new(env))
82
- configuration.build_options
83
- }
84
-
85
- # In the `shopify_app.rb` initializer:
86
77
  config.shop_session_repository = {YOUR_SHOP_MODEL_CLASS}
87
78
  config.user_session_repository = {YOUR_USER_MODEL_CLASS}
88
79
  ```
@@ -11,20 +11,42 @@
11
11
  A test helper that will allow you to test `ShopifyApp::WebhookVerification` in the controller from your app, to use this test, you need to `require` it directly inside your app `test/controllers/webhook_verification_test.rb`.
12
12
 
13
13
  ```ruby
14
- require 'test_helper'
15
- require 'action_controller'
16
- require 'action_controller/base'
17
- require 'shopify_app/test_helpers/webhook_verification_helper'
14
+ require 'test_helper'
15
+ require 'action_controller'
16
+ require 'action_controller/base'
17
+ require 'shopify_app/test_helpers/webhook_verification_helper'
18
18
  ```
19
19
 
20
- Or you can require in your `test/test_helper.rb`.
20
+ A test helper that allows you to stub out a shopify_app session in controllers that include `ShopifyApp::LoginProtection`, to use this helper, you need to `require` it directly.
21
+
22
+ Example Usage:
23
+
24
+ ```ruby
25
+ require 'shopify_app/test_helpers/shopify_session_helper'
26
+
27
+ class MyAuthenticatedControllerTest < ActionController::TestCase
28
+ include ShopifyApp::TestHelpers::ShopifySessionHelper
29
+
30
+ test "does not redirect when there is a valid shopify session" do
31
+ # note shop_domain should be the same as your shopify domain
32
+ shop_domain = "my-shop.myshopify.com"
33
+ setup_shopify_session(session_id: "1", shop_domain: shop_domain)
34
+
35
+ get :index
36
+
37
+ assert_response :ok
38
+ end
39
+ end
40
+ ```
41
+
42
+ Or you can require all shopify_app test helpers in your `test/test_helper.rb`.
21
43
 
22
44
  ```ruby
23
- ENV['RAILS_ENV'] ||= 'test'
24
- require_relative '../config/environment'
25
- require 'rails/test_help'
26
- require 'byebug'
27
- require 'shopify_app/test_helpers/all'
45
+ ENV['RAILS_ENV'] ||= 'test'
46
+ require_relative '../config/environment'
47
+ require 'rails/test_help'
48
+ require 'byebug'
49
+ require 'shopify_app/test_helpers/all'
28
50
  ```
29
51
 
30
52
  With `lib/shopify_app/test_helpers/all'` more tests can be added and will only need to be required in once in your library.
@@ -8,7 +8,9 @@ module ShopifyApp
8
8
  include ShopifyApp::SanitizedParams
9
9
 
10
10
  included do
11
- if ancestors.include?(ShopifyApp::RequireKnownShop || ShopifyApp::EnsureInstalled)
11
+ if defined?(ShopifyApp::RequireKnownShop) &&
12
+ defined?(ShopifyApp::EnsureInstalled) &&
13
+ ancestors.include?(ShopifyApp::RequireKnownShop || ShopifyApp::EnsureInstalled)
12
14
  message = <<~EOS
13
15
  We detected the use of incompatible concerns (RequireKnownShop/EnsureInstalled and LoginProtection) in #{name},
14
16
  which may lead to unpredictable behavior. In a future release of this library this will raise an error.
@@ -11,7 +11,11 @@ module ShopifyApp
11
11
  end
12
12
 
13
13
  def embedded_param?
14
- embedded_redirect_url? && params[:embedded].present? && params[:embedded] == "1"
14
+ embedded_redirect_url? && params[:embedded].present? && loaded_directly_from_admin?
15
+ end
16
+
17
+ def loaded_directly_from_admin?
18
+ ShopifyApp.configuration.embedded_app? && params[:embedded] == "1"
15
19
  end
16
20
 
17
21
  def redirect_for_embedded
@@ -21,7 +21,7 @@ module ShopifyApp
21
21
  end
22
22
 
23
23
  def recreate_webhooks!(session:)
24
- destroy_webhooks
24
+ destroy_webhooks(session: session)
25
25
  return unless ShopifyApp.configuration.has_webhooks?
26
26
 
27
27
  add_registrations
@@ -30,12 +30,12 @@ module ShopifyApp
30
30
  ShopifyAPI::Webhooks::Registry.register_all(session: session)
31
31
  end
32
32
 
33
- def destroy_webhooks
33
+ def destroy_webhooks(session:)
34
34
  return unless ShopifyApp.configuration.has_webhooks?
35
35
 
36
36
  ShopifyApp::Logger.debug("Destroying webhooks")
37
37
  ShopifyApp.configuration.webhooks.each do |attributes|
38
- ShopifyAPI::Webhooks::Registry.unregister(topic: attributes[:topic])
38
+ ShopifyAPI::Webhooks::Registry.unregister(topic: attributes[:topic], session: session)
39
39
  end
40
40
  end
41
41
 
@@ -34,8 +34,7 @@ module ShopifyApp
34
34
  def set_payload
35
35
  payload, _ = parse_token_data(ShopifyApp.configuration&.secret, ShopifyApp.configuration&.old_secret)
36
36
  @payload = validate_payload(payload)
37
- rescue *WARN_EXCEPTIONS => error
38
- ShopifyApp::Logger.warn("Failed to validate JWT: [#{error.class}] #{error}")
37
+ rescue *WARN_EXCEPTIONS
39
38
  nil
40
39
  end
41
40
 
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shopify_app/test_helpers/webhook_verification_helper"
4
+ require "shopify_app/test_helpers/shopify_session_helper"
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ module TestHelpers
5
+ module ShopifySessionHelper
6
+ def setup_shopify_session(session_id:, shop_domain:)
7
+ ShopifyAPI::Auth::Session.new(id: session_id, shop: shop_domain).tap do |session|
8
+ ShopifyApp::SessionRepository.stubs(:load_session).returns(session)
9
+ ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(session.id)
10
+ ShopifyAPI::Context.activate_session(session)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,38 +2,69 @@
2
2
 
3
3
  module ShopifyApp
4
4
  module Utils
5
- TRUSTED_SHOPIFY_DOMAINS = [
6
- "shopify.com",
7
- "myshopify.io",
8
- "myshopify.com",
9
- ].freeze
10
-
11
- def self.sanitize_shop_domain(shop_domain)
12
- myshopify_domain = ShopifyApp.configuration.myshopify_domain
13
- name = shop_domain.to_s.downcase.strip
14
- name += ".#{myshopify_domain}" if !name.include?(myshopify_domain.to_s) && !name.include?(".")
15
- name.sub!(%r|https?://|, "")
16
- trusted_domains = TRUSTED_SHOPIFY_DOMAINS.dup.push(myshopify_domain)
17
-
18
- u = URI("http://#{name}")
19
- regex = /^[a-z0-9][a-z0-9\-]*[a-z0-9]\.(#{trusted_domains.join("|")})$/
20
- u.host if u.host&.match(regex)
21
- rescue URI::InvalidURIError
22
- nil
23
- end
5
+ class << self
6
+ TRUSTED_SHOPIFY_DOMAINS = [
7
+ "shopify.com",
8
+ "myshopify.io",
9
+ "myshopify.com",
10
+ "spin.dev",
11
+ ].freeze
12
+
13
+ def sanitize_shop_domain(shop_domain)
14
+ uri = uri_from_shop_domain(shop_domain)
15
+ return nil if uri.nil?
16
+
17
+ trusted_domains.each do |trusted_domain|
18
+ no_shop_name_in_subdomain = uri.host == trusted_domain
19
+ from_trusted_domain = trusted_domain == uri.domain
20
+
21
+ return nil if no_shop_name_in_subdomain || uri.host&.empty?
22
+ return uri.host if from_trusted_domain
23
+ end
24
+
25
+ nil
26
+ end
27
+
28
+ def shop_login_url(shop:, host:, return_to:)
29
+ return ShopifyApp.configuration.login_url unless shop
30
+
31
+ url = URI(ShopifyApp.configuration.login_url)
32
+
33
+ url.query = URI.encode_www_form(
34
+ shop: shop,
35
+ host: host,
36
+ return_to: return_to,
37
+ )
38
+
39
+ url.to_s
40
+ end
41
+
42
+ private
43
+
44
+ def myshopify_domain
45
+ ShopifyApp.configuration.myshopify_domain
46
+ end
24
47
 
25
- def self.shop_login_url(shop:, host:, return_to:)
26
- return ShopifyApp.configuration.login_url unless shop
48
+ def trusted_domains
49
+ trusted_domains = TRUSTED_SHOPIFY_DOMAINS.dup
50
+ trusted_domains.append(myshopify_domain).uniq! if myshopify_domain
51
+ trusted_domains
52
+ end
27
53
 
28
- url = URI(ShopifyApp.configuration.login_url)
54
+ def uri_from_shop_domain(shop_domain)
55
+ name = shop_domain.to_s.downcase.strip
56
+ name += ".#{myshopify_domain}" if !name.include?(myshopify_domain.to_s) && !name.include?(".")
57
+ uri = Addressable::URI.parse(name)
29
58
 
30
- url.query = URI.encode_www_form(
31
- shop: shop,
32
- host: host,
33
- return_to: return_to,
34
- )
59
+ if uri.scheme.nil?
60
+ name = "https://" + name
61
+ uri = Addressable::URI.parse(name)
62
+ end
35
63
 
36
- url.to_s
64
+ uri
65
+ rescue Addressable::URI::InvalidURIError
66
+ nil
67
+ end
37
68
  end
38
69
  end
39
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyApp
4
- VERSION = "21.3.1"
4
+ VERSION = "21.4.0"
5
5
  end
data/lib/shopify_app.rb CHANGED
@@ -5,6 +5,7 @@ require "shopify_app/version"
5
5
  # deps
6
6
  require "shopify_api"
7
7
  require "redirect_safely"
8
+ require "addressable"
8
9
 
9
10
  module ShopifyApp
10
11
  def self.rails6?
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shopify_app",
3
- "version": "21.3.1",
3
+ "version": "21.4.0",
4
4
  "repository": "git@github.com:Shopify/shopify_app.git",
5
5
  "author": "Shopify",
6
6
  "license": "MIT",
data/shopify_app.gemspec CHANGED
@@ -15,11 +15,12 @@ Gem::Specification.new do |s|
15
15
  s.metadata["allowed_push_host"] = "https://rubygems.org"
16
16
 
17
17
  s.add_runtime_dependency("activeresource") # TODO: Remove this once all active resource dependencies are removed
18
+ s.add_runtime_dependency("addressable", "~> 2.7")
18
19
  s.add_runtime_dependency("browser_sniffer", "~> 2.0")
19
20
  s.add_runtime_dependency("jwt", ">= 2.2.3")
20
21
  s.add_runtime_dependency("rails", "> 5.2.1")
21
22
  s.add_runtime_dependency("redirect_safely", "~> 1.0")
22
- s.add_runtime_dependency("shopify_api", "~> 12.3")
23
+ s.add_runtime_dependency("shopify_api", "~> 12.4")
23
24
  s.add_runtime_dependency("sprockets-rails", ">= 2.0.0")
24
25
 
25
26
  s.add_development_dependency("byebug")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_app
3
3
  version: !ruby/object:Gem::Version
4
- version: 21.3.1
4
+ version: 21.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-12 00:00:00.000000000 Z
11
+ date: 2023-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeresource
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: addressable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: browser_sniffer
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +100,14 @@ dependencies:
86
100
  requirements:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '12.3'
103
+ version: '12.4'
90
104
  type: :runtime
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: '12.3'
110
+ version: '12.4'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: sprockets-rails
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -270,6 +284,7 @@ extra_rdoc_files: []
270
284
  files:
271
285
  - ".babelrc"
272
286
  - ".github/CODEOWNERS"
287
+ - ".github/ISSUE_TEMPLATE/ENHANCEMENT.md"
273
288
  - ".github/ISSUE_TEMPLATE/bug-report.md"
274
289
  - ".github/ISSUE_TEMPLATE/config.yml"
275
290
  - ".github/ISSUE_TEMPLATE/feature-request.md"
@@ -298,16 +313,7 @@ files:
298
313
  - app/assets/javascripts/shopify_app/app_bridge_3.1.1.js
299
314
  - app/assets/javascripts/shopify_app/app_bridge_redirect.js
300
315
  - app/assets/javascripts/shopify_app/app_bridge_utils_3.1.1.js
301
- - app/assets/javascripts/shopify_app/enable_cookies.js
302
- - app/assets/javascripts/shopify_app/itp_helper.js
303
- - app/assets/javascripts/shopify_app/partition_cookies.js
304
- - app/assets/javascripts/shopify_app/post_redirect.js
305
316
  - app/assets/javascripts/shopify_app/redirect.js
306
- - app/assets/javascripts/shopify_app/request_storage_access.js
307
- - app/assets/javascripts/shopify_app/storage_access.js
308
- - app/assets/javascripts/shopify_app/storage_access_redirect.js
309
- - app/assets/javascripts/shopify_app/top_level.js
310
- - app/assets/javascripts/shopify_app/top_level_interaction.js
311
317
  - app/controllers/concerns/shopify_app/authenticated.rb
312
318
  - app/controllers/concerns/shopify_app/ensure_authenticated_links.rb
313
319
  - app/controllers/concerns/shopify_app/ensure_has_session.rb
@@ -325,11 +331,7 @@ files:
325
331
  - app/views/shopify_app/partials/_form_styles.html.erb
326
332
  - app/views/shopify_app/partials/_layout_styles.html.erb
327
333
  - app/views/shopify_app/partials/_typography_styles.html.erb
328
- - app/views/shopify_app/sessions/enable_cookies.html.erb
329
334
  - app/views/shopify_app/sessions/new.html.erb
330
- - app/views/shopify_app/sessions/request_storage_access.html.erb
331
- - app/views/shopify_app/sessions/top_level_interaction.html.erb
332
- - app/views/shopify_app/shared/post_redirect_to_auth_shopify.html.erb
333
335
  - app/views/shopify_app/shared/redirect.html.erb
334
336
  - config/locales/cs.yml
335
337
  - config/locales/da.yml
@@ -458,6 +460,7 @@ files:
458
460
  - lib/shopify_app/session/user_session_storage.rb
459
461
  - lib/shopify_app/session/user_session_storage_with_scopes.rb
460
462
  - lib/shopify_app/test_helpers/all.rb
463
+ - lib/shopify_app/test_helpers/shopify_session_helper.rb
461
464
  - lib/shopify_app/test_helpers/webhook_verification_helper.rb
462
465
  - lib/shopify_app/utils.rb
463
466
  - lib/shopify_app/version.rb
@@ -1,3 +0,0 @@
1
- //= require ./itp_helper.js
2
- //= require ./storage_access.js
3
- //= require ./partition_cookies.js
@@ -1,40 +0,0 @@
1
- (function() {
2
- function ITPHelper(opts) {
3
- this.itpContent = document.getElementById('TopLevelInteractionContent');
4
- this.itpAction = document.getElementById('TopLevelInteractionButton');
5
- this.redirectUrl = opts.redirectUrl;
6
- }
7
-
8
- ITPHelper.prototype.redirect = function() {
9
- sessionStorage.setItem('shopify.top_level_interaction', true);
10
- window.location.href = this.redirectUrl;
11
- }
12
-
13
- ITPHelper.prototype.userAgentIsAffected = function() {
14
- return Boolean(document.hasStorageAccess);
15
- }
16
-
17
- ITPHelper.prototype.canPartitionCookies = function() {
18
- var versionRegEx = /Version\/12\.0\.?\d? Safari/;
19
- return versionRegEx.test(navigator.userAgent);
20
- }
21
-
22
- ITPHelper.prototype.setUpContent = function(onClick) {
23
- this.itpContent.style.display = 'block';
24
- this.itpAction.addEventListener('click', this.redirect.bind(this));
25
- }
26
-
27
- ITPHelper.prototype.execute = function() {
28
- if (!this.itpContent) {
29
- return;
30
- }
31
-
32
- if (this.userAgentIsAffected()) {
33
- this.setUpContent();
34
- } else {
35
- this.redirect();
36
- }
37
- }
38
-
39
- this.ITPHelper = ITPHelper;
40
- })(window);
@@ -1,8 +0,0 @@
1
- (function() {
2
- document.addEventListener("DOMContentLoaded", function() {
3
- var redirectTargetElement = document.getElementById("redirection-target");
4
- var targetInfo = JSON.parse(redirectTargetElement.dataset.target)
5
- var storageAccessHelper = new StorageAccessHelper(targetInfo);
6
- storageAccessHelper.execute();
7
- });
8
- })();
@@ -1,9 +0,0 @@
1
- (function() {
2
- function redirect() {
3
- var form = document.getElementById("redirect-form");
4
- if (form) {
5
- form.submit();
6
- }
7
- }
8
- document.addEventListener("DOMContentLoaded", redirect);
9
- })();
@@ -1,3 +0,0 @@
1
- //= require ./itp_helper.js
2
- //= require ./storage_access.js
3
- //= require ./storage_access_redirect.js
@@ -1,148 +0,0 @@
1
- //= require ./app_bridge_redirect.js
2
-
3
- (function() {
4
- var ACCESS_GRANTED_STATUS = 'storage_access_granted';
5
- var ACCESS_DENIED_STATUS = 'storage_access_denied';
6
-
7
- function StorageAccessHelper(redirectData) {
8
- this.redirectData = redirectData;
9
- }
10
-
11
- StorageAccessHelper.prototype.setNormalizedLink = function(storageAccessStatus) {
12
- return storageAccessStatus === ACCESS_GRANTED_STATUS ? this.redirectData.hasStorageAccessUrl : this.redirectData.doesNotHaveStorageAccessUrl;
13
- }
14
-
15
- StorageAccessHelper.prototype.redirectToAppTLD = function(storageAccessStatus) {
16
- var normalizedLink = document.createElement('a');
17
-
18
- window.appBridgeRedirect(this.setNormalizedLink(storageAccessStatus));
19
- }
20
-
21
- StorageAccessHelper.prototype.redirectToAppsIndex = function() {
22
- window.parent.location.href = this.redirectData.myshopifyUrl + '/admin/apps';
23
- }
24
-
25
- StorageAccessHelper.prototype.redirectToAppTargetUrl = function() {
26
- window.location.href = this.redirectData.appTargetUrl;
27
- }
28
-
29
- StorageAccessHelper.prototype.sameSiteNoneIncompatible = function(ua) {
30
- return ua.includes("iPhone OS 12_") || ua.includes("iPad; CPU OS 12_") || //iOS 12
31
- (ua.includes("UCBrowser/")
32
- ? this.isOlderUcBrowser(ua) //UC Browser < 12.13.2
33
- : (ua.includes("Chrome/5") || ua.includes("Chrome/6"))) ||
34
- ua.includes("Chromium/5") || ua.includes("Chromium/6") ||
35
- (ua.includes(" OS X 10_14_") &&
36
- ((ua.includes("Version/") && ua.includes("Safari")) || //Safari on MacOS 10.14
37
- ua.endsWith("(KHTML, like Gecko)"))); //Web view on MacOS 10.14
38
- }
39
-
40
- StorageAccessHelper.prototype.isOlderUcBrowser = function(ua) {
41
- var match = ua.match(/UCBrowser\/(\d+)\.(\d+)\.(\d+)\./);
42
- if (!match) return false;
43
- var major = parseInt(match[1]);
44
- var minor = parseInt(match[2]);
45
- var build = parseInt(match[3]);
46
- if (major != 12) return major < 12;
47
- if (minor != 13) return minor < 13;
48
- return build < 2;
49
- }
50
-
51
- StorageAccessHelper.prototype.setCookie = function(value) {
52
- if(!this.sameSiteNoneIncompatible(navigator.userAgent)) {
53
- value += '; secure; SameSite=None'
54
- }
55
- document.cookie = value;
56
- }
57
-
58
- StorageAccessHelper.prototype.grantedStorageAccess = function() {
59
- try {
60
- sessionStorage.setItem('shopify.granted_storage_access', true);
61
- this.setCookie('shopify.granted_storage_access=true');
62
- if (!document.cookie) {
63
- throw 'Cannot set third-party cookie.'
64
- }
65
- this.redirectToAppTargetUrl();
66
- } catch (error) {
67
- console.warn('Third party cookies may be blocked.', error);
68
- this.redirectToAppTLD(ACCESS_DENIED_STATUS);
69
- }
70
- }
71
-
72
- StorageAccessHelper.prototype.handleRequestStorageAccess = function() {
73
- return document.requestStorageAccess().then(this.grantedStorageAccess.bind(this), this.redirectToAppsIndex.bind(this, ACCESS_DENIED_STATUS));
74
- }
75
-
76
- StorageAccessHelper.prototype.setupRequestStorageAccess = function() {
77
- var requestContent = document.getElementById('RequestStorageAccess');
78
- var requestButton = document.getElementById('TriggerAllowCookiesPrompt');
79
-
80
- requestButton.addEventListener('click', this.handleRequestStorageAccess.bind(this));
81
- requestContent.style.display = 'block';
82
- }
83
-
84
- StorageAccessHelper.prototype.handleHasStorageAccess = function() {
85
- if (sessionStorage.getItem('shopify.granted_storage_access')) {
86
- // If app was classified by ITP and used Storage Access API to acquire access
87
- this.redirectToAppTargetUrl();
88
- } else {
89
- // If app has not been classified by ITP and still has storage access
90
- this.redirectToAppTLD(ACCESS_GRANTED_STATUS);
91
- }
92
- }
93
-
94
- StorageAccessHelper.prototype.handleGetStorageAccess = function() {
95
- if (sessionStorage.getItem('shopify.top_level_interaction')) {
96
- // If merchant has been redirected to interact with TLD (requirement for prompting request to gain storage access)
97
- this.setupRequestStorageAccess();
98
- } else {
99
- // If merchant has not been redirected to interact with TLD (requirement for prompting request to gain storage access)
100
- this.redirectToAppTLD(ACCESS_DENIED_STATUS);
101
- }
102
- }
103
-
104
- StorageAccessHelper.prototype.manageStorageAccess = function() {
105
- return document.hasStorageAccess().then(function(hasAccess) {
106
- if (hasAccess) {
107
- this.handleHasStorageAccess();
108
- } else {
109
- this.handleGetStorageAccess();
110
- }
111
- }.bind(this));
112
- }
113
-
114
- StorageAccessHelper.prototype.execute = function() {
115
- if (ITPHelper.prototype.canPartitionCookies()) {
116
- this.setUpCookiePartitioning();
117
- return;
118
- }
119
-
120
- if (ITPHelper.prototype.userAgentIsAffected()) {
121
- this.manageStorageAccess();
122
- } else {
123
- this.grantedStorageAccess();
124
- }
125
- }
126
-
127
- /* ITP 2.0 solution: handles cookie partitioning */
128
- StorageAccessHelper.prototype.setUpHelper = function() {
129
- var shopifyData = document.body.dataset;
130
- return new ITPHelper({redirectUrl: "https://" + shopifyData.shopOrigin + "/admin/apps/" + shopifyData.apiKey + shopifyData.returnTo});
131
- }
132
-
133
- StorageAccessHelper.prototype.setCookieAndRedirect = function() {
134
- this.setCookie('shopify.cookies_persist=true');
135
- var helper = this.setUpHelper();
136
- helper.redirect();
137
- }
138
-
139
- StorageAccessHelper.prototype.setUpCookiePartitioning = function() {
140
- var itpContent = document.getElementById('CookiePartitionPrompt');
141
- itpContent.style.display = 'block';
142
-
143
- var button = document.getElementById('AcceptCookies');
144
- button.addEventListener('click', this.setCookieAndRedirect.bind(this));
145
- }
146
-
147
- this.StorageAccessHelper = StorageAccessHelper;
148
- })(window);
@@ -1,17 +0,0 @@
1
- (function() {
2
- function redirect() {
3
- var redirectTargetElement = document.getElementById("redirection-target");
4
-
5
- var targetInfo = JSON.parse(redirectTargetElement.dataset.target)
6
-
7
- if (window.top == window.self) {
8
- // If the current window is the 'parent', change the URL by setting location.href
9
- window.top.location.href = targetInfo.hasStorageAccessUrl;
10
- } else {
11
- var storageAccessHelper = new StorageAccessHelper(targetInfo);
12
- storageAccessHelper.execute();
13
- }
14
- }
15
-
16
- document.addEventListener("DOMContentLoaded", redirect);
17
- })();
@@ -1,2 +0,0 @@
1
- //= require ./itp_helper.js
2
- //= require ./top_level_interaction.js
@@ -1,11 +0,0 @@
1
- (function() {
2
- function setUpTopLevelInteraction() {
3
- var TopLevelInteraction = new ITPHelper({
4
- redirectUrl: document.body.dataset.redirectUrl,
5
- });
6
-
7
- TopLevelInteraction.execute();
8
- }
9
-
10
- document.addEventListener("DOMContentLoaded", setUpTopLevelInteraction);
11
- })();
@@ -1,70 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="<%= I18n.locale %>">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <base target="_top">
7
- <title>Redirecting…</title>
8
- <%= render 'shopify_app/partials/layout_styles' %>
9
- <%= render 'shopify_app/partials/typography_styles' %>
10
- <%= render 'shopify_app/partials/card_styles' %>
11
- <%= render 'shopify_app/partials/button_styles' %>
12
- <style>
13
- #CookiePartitionPrompt {
14
- display: none;
15
- }
16
- </style>
17
-
18
- <%= javascript_include_tag('shopify_app/enable_cookies', crossorigin: 'anonymous', integrity: true) %>
19
- </head>
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
- <%=
22
- content_tag(
23
- :div, nil,
24
- id: 'redirection-target',
25
- data: {
26
- target: {
27
- myshopifyUrl: "https://#{current_shopify_domain}",
28
- hasStorageAccessUrl: "#{has_storage_access_url}",
29
- doesNotHaveStorageAccessUrl: "#{does_not_have_storage_access_url}",
30
- appTargetUrl: "#{app_target_url}"
31
- },
32
- },
33
- )
34
- %>
35
- <main id="CookiePartitionPrompt">
36
- <div class="Polaris-Page">
37
- <div class="Polaris-Page__Content">
38
- <div class="Polaris-Layout">
39
- <div class="Polaris-Layout__Section">
40
- <div class="Polaris-Stack Polaris-Stack--vertical">
41
- <div class="Polaris-Stack__Item">
42
- <div class="Polaris-Card">
43
- <div class="Polaris-Card__Header">
44
- <h1 class="Polaris-Heading"><%= I18n.t('enable_cookies_heading', app: ShopifyApp.configuration.application_name) %></h1>
45
- </div>
46
- <div class="Polaris-Card__Section">
47
- <p><%= I18n.t('enable_cookies_body', app: ShopifyApp.configuration.application_name) %></p>
48
- </div>
49
- <div class="Polaris-Card__Section Polaris-Card__Section--subdued">
50
- <p><%= I18n.t('enable_cookies_footer') %></p>
51
- </div>
52
- </div>
53
- </div>
54
- <div class="Polaris-Stack__Item">
55
- <div class="Polaris-Stack Polaris-Stack--distributionTrailing Polaris-Stack--distributionTrailingCustomSpacing">
56
- <div class="Polaris-Stack__Item">
57
- <button type="button" class="Polaris-Button Polaris-Button--primary" id="AcceptCookies">
58
- <span class="Polaris-Button__Content"><span><%= I18n.t('enable_cookies_action') %></span></span>
59
- </button>
60
- </div>
61
- </div>
62
- </div>
63
- </div>
64
- </div>
65
- </div>
66
- </div>
67
- </div>
68
- </main>
69
- </body>
70
- </html>
@@ -1,68 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="<%= I18n.locale %>">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <base target="_top">
7
- <title>Redirecting…</title>
8
- <%= render 'shopify_app/partials/layout_styles' %>
9
- <%= render 'shopify_app/partials/typography_styles' %>
10
- <%= render 'shopify_app/partials/card_styles' %>
11
- <%= render 'shopify_app/partials/button_styles' %>
12
- <style>
13
- #RequestStorageAccess {
14
- display: none;
15
- }
16
- </style>
17
- </head>
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
- <%=
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
- )
31
- %>
32
- <main id="RequestStorageAccess">
33
- <div class="Polaris-Page">
34
- <div class="Polaris-Page__Content">
35
- <div class="Polaris-Layout">
36
- <div class="Polaris-Layout__Section">
37
- <div class="Polaris-Stack Polaris-Stack--vertical">
38
- <div class="Polaris-Stack__Item">
39
- <div class="Polaris-Card">
40
- <div class="Polaris-Card__Header">
41
- <h1 class="Polaris-Heading"><%= I18n.t('request_storage_access_heading', app: ShopifyApp.configuration.application_name) %></h1>
42
- </div>
43
- <div class="Polaris-Card__Section">
44
- <p><%= I18n.t('request_storage_access_body', app: ShopifyApp.configuration.application_name) %></p>
45
- </div>
46
- <div class="Polaris-Card__Section Polaris-Card__Section--subdued">
47
- <p><%= I18n.t('request_storage_access_footer') %></p>
48
- </div>
49
- </div>
50
- </div>
51
- <div class="Polaris-Stack__Item">
52
- <div class="Polaris-Stack Polaris-Stack--distributionTrailing Polaris-Stack--distributionTrailingCustomSpacing">
53
- <div class="Polaris-Stack__Item">
54
- <button type="button" class="Polaris-Button Polaris-Button--primary" id="TriggerAllowCookiesPrompt">
55
- <span class="Polaris-Button__Content"><span><%= I18n.t('request_storage_access_action') %></span></span>
56
- </button>
57
- </div>
58
- </div>
59
- </div>
60
- </div>
61
- </div>
62
- </div>
63
- </div>
64
- </div>
65
- </main>
66
- <%= javascript_include_tag('shopify_app/request_storage_access', crossorigin: 'anonymous', integrity: true) %>
67
- </body>
68
- </html>
@@ -1,63 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="<%= I18n.locale %>">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <base target="_top">
7
- <title>Redirecting…</title>
8
- <%= render 'shopify_app/partials/card_styles' %>
9
- <%= render 'shopify_app/partials/layout_styles' %>
10
- <%= render 'shopify_app/partials/typography_styles' %>
11
- <%= render 'shopify_app/partials/button_styles' %>
12
- <%= render 'shopify_app/partials/empty_state_styles' %>
13
- <style>
14
- #TopLevelInteractionContent {
15
- display: none;
16
- }
17
- </style>
18
-
19
- <%= javascript_include_tag('shopify_app/top_level', crossorigin: 'anonymous', integrity: true) %>
20
- </head>
21
- <body data-api-key="<%= ShopifyApp.configuration.api_key %>" data-shop-origin="https://<%= @shop %>" data-host="<%= @host %>" data-redirect-url="<%= @url %>">
22
- <main id="TopLevelInteractionContent">
23
- <div class="Polaris-Page">
24
- <div class="Polaris-Page__Content">
25
- <div class="Polaris-Layout">
26
- <div class="Polaris-Layout__Section">
27
- <div class="Polaris-Stack Polaris-Stack--vertical">
28
- <div class="Polaris-Stack__Item">
29
- <div class="Polaris-Card">
30
- <div class="Polaris-Card__Section">
31
- <div class="Polaris-EmptyState">
32
- <div class="Polaris-EmptyState__Section">
33
- <div class="Polaris-EmptyState__DetailsContainer">
34
- <div class="Polaris-EmptyState__Details">
35
- <div class="Polaris-TextContainer">
36
- <h1 class="Polaris-DisplayText Polaris-DisplayText--sizeSmall"><%= I18n.t('top_level_interaction_heading', app: ShopifyApp.configuration.application_name) %></h1>
37
- <div class="Polaris-EmptyState__Content">
38
- <p><%= I18n.t('top_level_interaction_body', app: ShopifyApp.configuration.application_name) %></p>
39
- </div>
40
- </div>
41
- <div class="Polaris-EmptyState__Actions">
42
- <div class="Polaris-Stack Polaris-Stack--alignmentCenter">
43
- <div class="Polaris-Stack__Item"><button type="button" id="TopLevelInteractionButton" class="Polaris-Button Polaris-Button--primary Polaris-Button--sizeLarge"><span class="Polaris-Button__Content"><span class="Polaris-Button__Icon"></span><span><%= I18n.t('top_level_interaction_action') %></span></span></button></div>
44
- </div>
45
- </div>
46
- </div>
47
- </div>
48
- <div class="Polaris-EmptyState__ImageContainer">
49
- <%= image_tag 'storage_access.svg', role: "presentation", alt: "", class: "Polaris-EmptyState__Image" %>
50
- </div>
51
- </div>
52
- </div>
53
- </div>
54
- </div>
55
- </div>
56
- </div>
57
- </div>
58
- </div>
59
- </div>
60
- </div>
61
- </main>
62
- </body>
63
- </html>
@@ -1,13 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <base target="_top">
7
- <title>Redirecting…</title>
8
- <%= javascript_include_tag('shopify_app/post_redirect', crossorigin: 'anonymous', integrity: true) %>
9
- </head>
10
- <body>
11
- <%= form_tag '/auth/shopify', id: 'redirect-form' %>
12
- </body>
13
- </html>