shopify_app 11.5.1 → 12.0.1

Sign up to get free protection for your applications and to get access to all the features.
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 %>