shopify_app 11.5.1 → 12.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/README.md +122 -115
  4. data/app/assets/javascripts/shopify_app/itp_helper.js +6 -6
  5. data/app/assets/javascripts/shopify_app/storage_access.js +35 -6
  6. data/app/controllers/concerns/shopify_app/authenticated.rb +1 -1
  7. data/app/controllers/shopify_app/callback_controller.rb +8 -2
  8. data/app/controllers/shopify_app/extension_verification_controller.rb +20 -0
  9. data/app/controllers/shopify_app/sessions_controller.rb +8 -6
  10. data/app/views/shopify_app/sessions/enable_cookies.html.erb +1 -1
  11. data/app/views/shopify_app/sessions/request_storage_access.html.erb +1 -1
  12. data/config/locales/pt-BR.yml +1 -1
  13. data/docs/Quickstart.md +44 -16
  14. data/docs/install-on-dev-shop.png +0 -0
  15. data/docs/test-your-app.png +0 -0
  16. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +2 -6
  17. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +0 -6
  18. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +2 -2
  19. data/lib/generators/shopify_app/install/templates/flash_messages.js +11 -2
  20. data/lib/generators/shopify_app/install/templates/shopify_app.js +9 -3
  21. data/lib/generators/shopify_app/install/templates/shopify_provider.rb +1 -0
  22. data/lib/generators/shopify_app/user_model/templates/db/migrate/create_users.erb +16 -0
  23. data/lib/generators/shopify_app/user_model/templates/user.rb +7 -0
  24. data/lib/generators/shopify_app/user_model/templates/users.yml +4 -0
  25. data/lib/generators/shopify_app/user_model/user_model_generator.rb +38 -0
  26. data/lib/shopify_app.rb +5 -3
  27. data/lib/shopify_app/configuration.rb +10 -0
  28. data/lib/shopify_app/controller_concerns/login_protection.rb +33 -6
  29. data/lib/shopify_app/engine.rb +4 -0
  30. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +60 -0
  31. data/lib/shopify_app/session/in_memory_session_store.rb +1 -1
  32. data/lib/shopify_app/session/session_repository.rb +2 -2
  33. data/lib/shopify_app/session/session_storage.rb +10 -22
  34. data/lib/shopify_app/session/storage_strategies/shop_storage_strategy.rb +23 -0
  35. data/lib/shopify_app/session/storage_strategies/user_storage_strategy.rb +24 -0
  36. data/lib/shopify_app/version.rb +1 -1
  37. data/package.json +1 -0
  38. data/service.yml +1 -1
  39. data/shopify_app.gemspec +5 -2
  40. metadata +56 -6
  41. data/lib/generators/shopify_app/home_controller/templates/shopify_app_ready_script.html.erb +0 -7
  42. data/lib/shopify_app/controllers/extension_verification_controller.rb +0 -18
@@ -27,12 +27,30 @@ module ShopifyApp
27
27
  end
28
28
 
29
29
  def shop_session
30
- return unless session[:shopify]
31
- @shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify])
30
+ if ShopifyApp.configuration.per_user_tokens?
31
+ return unless session[:shopify_user]
32
+ @shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify_user]['id'])
33
+ else
34
+ return unless session[:shopify]
35
+ @shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify])
36
+ end
32
37
  end
33
38
 
34
- def login_again_if_different_shop
39
+ def login_again_if_different_user_or_shop
40
+ if ShopifyApp.configuration.per_user_tokens?
41
+ valid_session_data = session[:user_session].present? && params[:session].present? # session data was sent/stored correctly
42
+ sessions_do_not_match = session[:user_session] != params[:session] # current user is different from stored user
43
+
44
+ if valid_session_data && sessions_do_not_match
45
+ clear_session = true
46
+ end
47
+ end
48
+
35
49
  if shop_session && params[:shop] && params[:shop].is_a?(String) && (shop_session.domain != params[:shop])
50
+ clear_session = true
51
+ end
52
+
53
+ if clear_session
36
54
  clear_shop_session
37
55
  redirect_to_login
38
56
  end
@@ -45,8 +63,14 @@ module ShopifyApp
45
63
  head :unauthorized
46
64
  else
47
65
  if request.get?
48
- session[:return_to] = "#{request.path}?#{sanitized_params.to_query}"
66
+ path = request.path
67
+ query = sanitized_params.to_query
68
+ else
69
+ referer = URI(request.referer || "/")
70
+ path = referer.path
71
+ query = "#{referer.query}&#{sanitized_params.to_query}"
49
72
  end
73
+ session[:return_to] = "#{path}?#{query}"
50
74
  redirect_to(login_url_with_optional_shop)
51
75
  end
52
76
  end
@@ -60,6 +84,7 @@ module ShopifyApp
60
84
  session[:shopify] = nil
61
85
  session[:shopify_domain] = nil
62
86
  session[:shopify_user] = nil
87
+ session[:user_session] = nil
63
88
  end
64
89
 
65
90
  def login_url_with_optional_shop(top_level: false)
@@ -75,8 +100,10 @@ module ShopifyApp
75
100
  query_params = {}
76
101
  query_params[:shop] = sanitized_params[:shop] if params[:shop].present?
77
102
 
78
- if session[:return_to] && return_to_param_required?
79
- query_params[:return_to] = session[:return_to]
103
+ return_to = session[:return_to] || params[:return_to]
104
+
105
+ if return_to.present? && return_to_param_required?
106
+ query_params[:return_to] = return_to
80
107
  end
81
108
 
82
109
  has_referer_shop_name = referer_sanitized_shop_name.present?
@@ -12,5 +12,9 @@ module ShopifyApp
12
12
  storage_access.svg
13
13
  ]
14
14
  end
15
+
16
+ initializer "shopify_app.middleware" do |app|
17
+ app.config.middleware.insert_before(ActionDispatch::Cookies, ShopifyApp::SameSiteCookieMiddleware)
18
+ end
15
19
  end
16
20
  end
@@ -0,0 +1,60 @@
1
+ module ShopifyApp
2
+ class SameSiteCookieMiddleware
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ _status, headers, _body = @app.call(env)
9
+ ensure
10
+ user_agent = env['HTTP_USER_AGENT']
11
+
12
+ if headers && headers['Set-Cookie'] && !SameSiteCookieMiddleware.same_site_none_incompatible?(user_agent) &&
13
+ ShopifyApp.configuration.enable_same_site_none
14
+
15
+ cookies = headers['Set-Cookie'].split("\n").compact
16
+
17
+ cookies.each do |cookie|
18
+ unless cookie.include?("; SameSite")
19
+ headers['Set-Cookie'] = headers['Set-Cookie'].gsub(cookie, "#{cookie}; secure; SameSite=None")
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.same_site_none_incompatible?(user_agent)
26
+ sniffer = BrowserSniffer.new(user_agent)
27
+
28
+ webkit_same_site_bug?(sniffer) || drops_unrecognized_same_site_cookies?(sniffer)
29
+ rescue
30
+ true
31
+ end
32
+
33
+ def self.webkit_same_site_bug?(sniffer)
34
+ (sniffer.os == :ios && sniffer.os_version.match(/^([0-9]|1[12])[\.\_]/)) ||
35
+ (sniffer.os == :mac && sniffer.browser == :safari && sniffer.os_version.match(/^10[\.\_]14/))
36
+ end
37
+
38
+ def self.drops_unrecognized_same_site_cookies?(sniffer)
39
+ (chromium_based?(sniffer) && sniffer.major_browser_version >= 51 && sniffer.major_browser_version <= 66) ||
40
+ (uc_browser?(sniffer) && !uc_browser_version_at_least?(sniffer: sniffer, major: 12, minor: 13, build: 2))
41
+ end
42
+
43
+ def self.chromium_based?(sniffer)
44
+ sniffer.browser_name.downcase.match(/chrom(e|ium)/)
45
+ end
46
+
47
+ def self.uc_browser?(sniffer)
48
+ sniffer.user_agent.downcase.match(/uc\s?browser/)
49
+ end
50
+
51
+ def self.uc_browser_version_at_least?(sniffer:, major:, minor:, build:)
52
+ digits = sniffer.browser_version.split('.').map(&:to_i)
53
+ return false unless digits.count >= 3
54
+
55
+ return digits[0] > major if digits[0] != major
56
+ return digits[1] > minor if digits[1] != minor
57
+ digits[2] >= build
58
+ end
59
+ end
60
+ end
@@ -6,7 +6,7 @@ module ShopifyApp
6
6
  repo[id]
7
7
  end
8
8
 
9
- def self.store(session)
9
+ def self.store(session, *args)
10
10
  id = SecureRandom.uuid
11
11
  repo[id] = session
12
12
  id
@@ -15,8 +15,8 @@ module ShopifyApp
15
15
  storage.retrieve(id)
16
16
  end
17
17
 
18
- def store(session)
19
- storage.store(session)
18
+ def store(session, *args)
19
+ storage.store(session, *args)
20
20
  end
21
21
 
22
22
  def storage
@@ -3,9 +3,18 @@ module ShopifyApp
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- validates :shopify_domain, presence: true, uniqueness: { case_sensitive: false }
6
+ if ShopifyApp.configuration.per_user_tokens?
7
+ extend ShopifyApp::SessionStorage::UserStorageStrategy
8
+ else
9
+ extend ShopifyApp::SessionStorage::ShopStorageStrategy
10
+ end
11
+
7
12
  validates :shopify_token, presence: true
8
13
  validates :api_version, presence: true
14
+ validates :shopify_domain, presence: true,
15
+ if: Proc.new {|_| ShopifyApp.configuration.per_user_tokens? }
16
+ validates :shopify_domain, presence: true, uniqueness: { case_sensitive: false },
17
+ if: Proc.new {|_| !ShopifyApp.configuration.per_user_tokens? }
9
18
  end
10
19
 
11
20
  def with_shopify_session(&block)
@@ -16,26 +25,5 @@ module ShopifyApp
16
25
  &block
17
26
  )
18
27
  end
19
-
20
- class_methods do
21
- def store(session)
22
- shop = find_or_initialize_by(shopify_domain: session.domain)
23
- shop.shopify_token = session.token
24
- shop.save!
25
- shop.id
26
- end
27
-
28
- def retrieve(id)
29
- return unless id
30
-
31
- if shop = self.find_by(id: id)
32
- ShopifyAPI::Session.new(
33
- domain: shop.shopify_domain,
34
- token: shop.shopify_token,
35
- api_version: shop.api_version
36
- )
37
- end
38
- end
39
- end
40
28
  end
41
29
  end
@@ -0,0 +1,23 @@
1
+ module ShopifyApp
2
+ module SessionStorage
3
+ module ShopStorageStrategy
4
+ def store(auth_session, *args)
5
+ shop = find_or_initialize_by(shopify_domain: auth_session.domain)
6
+ shop.shopify_token = auth_session.token
7
+ shop.save!
8
+ shop.id
9
+ end
10
+
11
+ def retrieve(id)
12
+ return unless id
13
+ if shop = self.find_by(id: id)
14
+ ShopifyAPI::Session.new(
15
+ domain: shop.shopify_domain,
16
+ token: shop.shopify_token,
17
+ api_version: shop.api_version
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module ShopifyApp
2
+ module SessionStorage
3
+ module UserStorageStrategy
4
+ def store(auth_session, user)
5
+ user = find_or_initialize_by(shopify_user_id: user[:id])
6
+ user.shopify_token = auth_session.token
7
+ user.shopify_domain = auth_session.domain
8
+ user.save!
9
+ user.id
10
+ end
11
+
12
+ def retrieve(id)
13
+ return unless id
14
+ if user = self.find_by(shopify_user_id: id)
15
+ ShopifyAPI::Session.new(
16
+ domain: user.shopify_domain,
17
+ token: user.shopify_token,
18
+ api_version: user.api_version
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module ShopifyApp
2
- VERSION = '11.5.1'.freeze
2
+ VERSION = '12.0.1'.freeze
3
3
  end
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "shopify_app",
3
+ "version": "12.0.1",
3
4
  "repository": "git@github.com:Shopify/shopify_app.git",
4
5
  "author": "Shopify",
5
6
  "license": "MIT",
@@ -2,6 +2,6 @@ audience: partner
2
2
  classification: library
3
3
  org_line: App & Partner Platform
4
4
  owners:
5
- - Shopify/app-partner-dev-tools-education
5
+ - Shopify/platform-dev-tools-education
6
6
  slack_channels:
7
7
  - dev-tools-education
@@ -12,12 +12,15 @@ Gem::Specification.new do |s|
12
12
 
13
13
  s.add_runtime_dependency('browser_sniffer', '~> 1.1.3')
14
14
  s.add_runtime_dependency('rails', '> 5.2.1')
15
- s.add_runtime_dependency('shopify_api', '~> 8.0')
15
+ s.add_runtime_dependency('shopify_api', '~> 9.0')
16
16
  s.add_runtime_dependency('omniauth-shopify-oauth2', '~> 2.2.0')
17
17
 
18
18
  s.add_development_dependency('rake')
19
19
  s.add_development_dependency('byebug')
20
20
  s.add_development_dependency('pry')
21
+ s.add_development_dependency('pry-nav')
22
+ s.add_development_dependency('pry-stack_explorer')
23
+ s.add_development_dependency('rb-readline')
21
24
  s.add_development_dependency('sqlite3', '~> 1.4')
22
25
  s.add_development_dependency('minitest')
23
26
  s.add_development_dependency('mocha')
@@ -26,4 +29,4 @@ Gem::Specification.new do |s|
26
29
  s.files = `git ls-files`.split("\n").reject { |f| f.match(%r{^(test|example)/}) }
27
30
  s.test_files = `git ls-files -- {test}/*`.split("\n")
28
31
  s.require_paths = ["lib"]
29
- end
32
+ end
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: 11.5.1
4
+ version: 12.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-08 00:00:00.000000000 Z
11
+ date: 2020-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: browser_sniffer
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '8.0'
47
+ version: '9.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '8.0'
54
+ version: '9.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: omniauth-shopify-oauth2
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,48 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-nav
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry-stack_explorer
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rb-readline
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
111
153
  - !ruby/object:Gem::Dependency
112
154
  name: sqlite3
113
155
  requirement: !ruby/object:Gem::Requirement
@@ -197,6 +239,7 @@ files:
197
239
  - app/controllers/concerns/shopify_app/authenticated.rb
198
240
  - app/controllers/shopify_app/authenticated_controller.rb
199
241
  - app/controllers/shopify_app/callback_controller.rb
242
+ - app/controllers/shopify_app/extension_verification_controller.rb
200
243
  - app/controllers/shopify_app/sessions_controller.rb
201
244
  - app/controllers/shopify_app/webhooks_controller.rb
202
245
  - app/views/shopify_app/partials/_button_styles.html.erb
@@ -235,6 +278,8 @@ files:
235
278
  - docs/Quickstart.md
236
279
  - docs/Releasing.md
237
280
  - docs/Troubleshooting.md
281
+ - docs/install-on-dev-shop.png
282
+ - docs/test-your-app.png
238
283
  - images/app-proxy-screenshot.png
239
284
  - karma.conf.js
240
285
  - lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb
@@ -253,7 +298,6 @@ files:
253
298
  - lib/generators/shopify_app/home_controller/home_controller_generator.rb
254
299
  - lib/generators/shopify_app/home_controller/templates/home_controller.rb
255
300
  - lib/generators/shopify_app/home_controller/templates/index.html.erb
256
- - lib/generators/shopify_app/home_controller/templates/shopify_app_ready_script.html.erb
257
301
  - lib/generators/shopify_app/install/install_generator.rb
258
302
  - lib/generators/shopify_app/install/templates/_flash_messages.html.erb
259
303
  - lib/generators/shopify_app/install/templates/embedded_app.html.erb
@@ -275,6 +319,10 @@ files:
275
319
  - lib/generators/shopify_app/shop_model/templates/shop.rb
276
320
  - lib/generators/shopify_app/shop_model/templates/shops.yml
277
321
  - lib/generators/shopify_app/shopify_app_generator.rb
322
+ - lib/generators/shopify_app/user_model/templates/db/migrate/create_users.erb
323
+ - lib/generators/shopify_app/user_model/templates/user.rb
324
+ - lib/generators/shopify_app/user_model/templates/users.yml
325
+ - lib/generators/shopify_app/user_model/user_model_generator.rb
278
326
  - lib/generators/shopify_app/views/views_generator.rb
279
327
  - lib/shopify_app.rb
280
328
  - lib/shopify_app/configuration.rb
@@ -284,15 +332,17 @@ files:
284
332
  - lib/shopify_app/controller_concerns/localization.rb
285
333
  - lib/shopify_app/controller_concerns/login_protection.rb
286
334
  - lib/shopify_app/controller_concerns/webhook_verification.rb
287
- - lib/shopify_app/controllers/extension_verification_controller.rb
288
335
  - lib/shopify_app/engine.rb
289
336
  - lib/shopify_app/jobs/scripttags_manager_job.rb
290
337
  - lib/shopify_app/jobs/webhooks_manager_job.rb
291
338
  - lib/shopify_app/managers/scripttags_manager.rb
292
339
  - lib/shopify_app/managers/webhooks_manager.rb
340
+ - lib/shopify_app/middleware/same_site_cookie_middleware.rb
293
341
  - lib/shopify_app/session/in_memory_session_store.rb
294
342
  - lib/shopify_app/session/session_repository.rb
295
343
  - lib/shopify_app/session/session_storage.rb
344
+ - lib/shopify_app/session/storage_strategies/shop_storage_strategy.rb
345
+ - lib/shopify_app/session/storage_strategies/user_storage_strategy.rb
296
346
  - lib/shopify_app/utils.rb
297
347
  - lib/shopify_app/version.rb
298
348
  - package-lock.json
@@ -1,7 +0,0 @@
1
- <% content_for :javascript do %>
2
- <script type="text/javascript">
3
- ShopifyApp.ready(function(){
4
- ShopifyApp.Bar.initialize({ title: "Home" });
5
- });
6
- </script>
7
- <% end %>