shopify_app 17.0.3 → 17.2.0

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +63 -0
  4. data/.github/ISSUE_TEMPLATE/config.yml +1 -0
  5. data/.github/ISSUE_TEMPLATE/feature-request.md +33 -0
  6. data/.github/PULL_REQUEST_TEMPLATE.md +17 -1
  7. data/.github/workflows/build.yml +4 -1
  8. data/CHANGELOG.md +22 -0
  9. data/CONTRIBUTING.md +76 -0
  10. data/Gemfile.lock +81 -80
  11. data/README.md +72 -593
  12. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +4 -0
  13. data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +32 -0
  14. data/app/controllers/shopify_app/callback_controller.rb +24 -2
  15. data/app/controllers/shopify_app/sessions_controller.rb +13 -4
  16. data/config/locales/nl.yml +1 -1
  17. data/docs/Quickstart.md +15 -77
  18. data/docs/Troubleshooting.md +129 -4
  19. data/docs/Upgrading.md +126 -0
  20. data/docs/shopify_app/authentication.md +124 -0
  21. data/docs/shopify_app/engine.md +82 -0
  22. data/docs/shopify_app/generators.md +127 -0
  23. data/docs/shopify_app/handling-access-scopes-changes.md +14 -0
  24. data/docs/shopify_app/script-tags.md +28 -0
  25. data/docs/shopify_app/session-repository.md +88 -0
  26. data/docs/shopify_app/testing.md +38 -0
  27. data/docs/shopify_app/webhooks.md +72 -0
  28. data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +2 -0
  29. data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +1 -0
  30. data/lib/generators/shopify_app/install/install_generator.rb +30 -1
  31. data/lib/generators/shopify_app/install/templates/omniauth.rb +1 -0
  32. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +5 -2
  33. data/lib/generators/shopify_app/install/templates/shopify_provider.rb.tt +8 -0
  34. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +27 -0
  35. data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_scopes_column.erb +5 -0
  36. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -1
  37. data/lib/generators/shopify_app/shopify_app_generator.rb +1 -1
  38. data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_access_scopes_column.erb +5 -0
  39. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -1
  40. data/lib/generators/shopify_app/user_model/user_model_generator.rb +27 -0
  41. data/lib/shopify_app.rb +10 -0
  42. data/lib/shopify_app/access_scopes/noop_strategy.rb +13 -0
  43. data/lib/shopify_app/access_scopes/shop_strategy.rb +24 -0
  44. data/lib/shopify_app/access_scopes/user_strategy.rb +41 -0
  45. data/lib/shopify_app/configuration.rb +22 -0
  46. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +1 -1
  47. data/lib/shopify_app/omniauth/omniauth_configuration.rb +64 -0
  48. data/lib/shopify_app/session/in_memory_shop_session_store.rb +9 -7
  49. data/lib/shopify_app/session/in_memory_user_session_store.rb +9 -7
  50. data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +58 -0
  51. data/lib/shopify_app/session/user_session_storage_with_scopes.rb +58 -0
  52. data/lib/shopify_app/utils.rb +12 -0
  53. data/lib/shopify_app/version.rb +1 -1
  54. data/package.json +1 -1
  55. data/shopify_app.gemspec +2 -2
  56. metadata +29 -10
  57. data/.github/ISSUE_TEMPLATE.md +0 -19
  58. data/docs/install-on-dev-shop.png +0 -0
  59. data/docs/test-your-app.png +0 -0
  60. data/lib/generators/shopify_app/install/templates/shopify_provider.rb +0 -20
@@ -9,6 +9,8 @@ module ShopifyApp
9
9
  attr_accessor :secret
10
10
  attr_accessor :old_secret
11
11
  attr_accessor :scope
12
+ attr_writer :shop_access_scopes
13
+ attr_writer :user_access_scopes
12
14
  attr_accessor :embedded_app
13
15
  alias_method :embedded_app?, :embedded_app
14
16
  attr_accessor :webhooks
@@ -16,6 +18,8 @@ module ShopifyApp
16
18
  attr_accessor :after_authenticate_job
17
19
  attr_accessor :api_version
18
20
 
21
+ attr_accessor :reauth_on_access_scope_changes
22
+
19
23
  # customise urls
20
24
  attr_accessor :root_url
21
25
  attr_writer :login_url
@@ -70,6 +74,16 @@ module ShopifyApp
70
74
  ShopifyApp::SessionRepository.shop_storage
71
75
  end
72
76
 
77
+ def shop_access_scopes_strategy
78
+ return ShopifyApp::AccessScopes::NoopStrategy unless reauth_on_access_scope_changes
79
+ ShopifyApp::AccessScopes::ShopStrategy
80
+ end
81
+
82
+ def user_access_scopes_strategy
83
+ return ShopifyApp::AccessScopes::NoopStrategy unless reauth_on_access_scope_changes
84
+ ShopifyApp::AccessScopes::UserStrategy
85
+ end
86
+
73
87
  def has_webhooks?
74
88
  webhooks.present?
75
89
  end
@@ -81,6 +95,14 @@ module ShopifyApp
81
95
  def enable_same_site_none
82
96
  !Rails.env.test? && (@enable_same_site_none.nil? ? embedded_app? : @enable_same_site_none)
83
97
  end
98
+
99
+ def shop_access_scopes
100
+ @shop_access_scopes || scope
101
+ end
102
+
103
+ def user_access_scopes
104
+ @user_access_scopes || scope
105
+ end
84
106
  end
85
107
 
86
108
  def self.configuration
@@ -21,7 +21,7 @@ module ShopifyApp
21
21
  .compact
22
22
  .map do |cookie|
23
23
  cookie << '; Secure' unless cookie =~ /;\s*secure/i
24
- cookie << '; SameSite=None' unless cookie =~ /;\s*samesite=/i
24
+ cookie << '; SameSite=None' if ShopifyApp.configuration.embedded_app?
25
25
  cookie
26
26
  end
27
27
 
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ class OmniAuthConfiguration
5
+ attr_reader :strategy, :request
6
+ attr_writer :client_options_site, :scopes, :per_user_permissions
7
+
8
+ def initialize(strategy, request)
9
+ @strategy = strategy
10
+ @request = request
11
+ end
12
+
13
+ def build_options
14
+ strategy.options[:client_options][:site] = client_options_site
15
+ strategy.options[:scope] = scopes
16
+ strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
17
+ strategy.options[:per_user_permissions] = request_online_tokens?
18
+ end
19
+
20
+ private
21
+
22
+ def request_online_tokens?
23
+ return @per_user_permissions unless @per_user_permissions.nil?
24
+ default_request_online_tokens?
25
+ end
26
+
27
+ def scopes
28
+ @scopes || default_scopes
29
+ end
30
+
31
+ def client_options_site
32
+ @client_options_site || default_client_options_site
33
+ end
34
+
35
+ def default_scopes
36
+ if request_online_tokens?
37
+ ShopifyApp.configuration.user_access_scopes
38
+ else
39
+ ShopifyApp.configuration.shop_access_scopes
40
+ end
41
+ end
42
+
43
+ def default_client_options_site
44
+ return '' unless shop_domain.present?
45
+ "https://#{shopify_auth_params[:shop]}"
46
+ end
47
+
48
+ def default_request_online_tokens?
49
+ strategy.session[:user_tokens] && !update_shop_scopes?
50
+ end
51
+
52
+ def update_shop_scopes?
53
+ ShopifyApp.configuration.shop_access_scopes_strategy.update_access_scopes?(shop_domain)
54
+ end
55
+
56
+ def shop_domain
57
+ request.params['shop'] || (shopify_auth_params && shopify_auth_params['shop'])
58
+ end
59
+
60
+ def shopify_auth_params
61
+ strategy.session['shopify.omniauth_params']&.with_indifferent_access
62
+ end
63
+ end
64
+ end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
  module ShopifyApp
3
3
  class InMemoryShopSessionStore < InMemorySessionStore
4
- def self.store(session, *args)
5
- id = super
6
- repo[session.domain] = session
7
- id
8
- end
4
+ class << self
5
+ def store(session, *args)
6
+ id = super
7
+ repo[session.domain] = session
8
+ id
9
+ end
9
10
 
10
- def self.retrieve_by_shopify_domain(shopify_domain)
11
- repo[shopify_domain]
11
+ def retrieve_by_shopify_domain(shopify_domain)
12
+ repo[shopify_domain]
13
+ end
12
14
  end
13
15
  end
14
16
  end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
  module ShopifyApp
3
3
  class InMemoryUserSessionStore < InMemorySessionStore
4
- def self.store(session, user)
5
- id = super
6
- repo[user.shopify_user_id] = session
7
- id
8
- end
4
+ class << self
5
+ def store(session, user)
6
+ id = super
7
+ repo[user.shopify_user_id] = session
8
+ id
9
+ end
9
10
 
10
- def self.retrieve_by_shopify_user_id(user_id)
11
- repo[user_id]
11
+ def retrieve_by_shopify_user_id(user_id)
12
+ repo[user_id]
13
+ end
12
14
  end
13
15
  end
14
16
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ module ShopSessionStorageWithScopes
5
+ extend ActiveSupport::Concern
6
+ include ::ShopifyApp::SessionStorage
7
+
8
+ included do
9
+ validates :shopify_domain, presence: true, uniqueness: { case_sensitive: false }
10
+ end
11
+
12
+ class_methods do
13
+ def store(auth_session, *_args)
14
+ shop = find_or_initialize_by(shopify_domain: auth_session.domain)
15
+ shop.shopify_token = auth_session.token
16
+ shop.access_scopes = auth_session.access_scopes
17
+
18
+ shop.save!
19
+ shop.id
20
+ end
21
+
22
+ def retrieve(id)
23
+ shop = find_by(id: id)
24
+ construct_session(shop)
25
+ end
26
+
27
+ def retrieve_by_shopify_domain(domain)
28
+ shop = find_by(shopify_domain: domain)
29
+ construct_session(shop)
30
+ end
31
+
32
+ private
33
+
34
+ def construct_session(shop)
35
+ return unless shop
36
+
37
+ ShopifyAPI::Session.new(
38
+ domain: shop.shopify_domain,
39
+ token: shop.shopify_token,
40
+ api_version: shop.api_version,
41
+ access_scopes: shop.access_scopes
42
+ )
43
+ end
44
+ end
45
+
46
+ def access_scopes=(scopes)
47
+ super(scopes)
48
+ rescue NotImplementedError, NoMethodError
49
+ raise NotImplementedError, "#access_scopes= must be defined to handle storing access scopes: #{scopes}"
50
+ end
51
+
52
+ def access_scopes
53
+ super
54
+ rescue NotImplementedError, NoMethodError
55
+ raise NotImplementedError, "#access_scopes= must be defined to hook into stored access scopes"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+ module ShopifyApp
3
+ module UserSessionStorageWithScopes
4
+ extend ActiveSupport::Concern
5
+ include ::ShopifyApp::SessionStorage
6
+
7
+ included do
8
+ validates :shopify_domain, presence: true
9
+ end
10
+
11
+ class_methods do
12
+ def store(auth_session, user)
13
+ user = find_or_initialize_by(shopify_user_id: user[:id])
14
+ user.shopify_token = auth_session.token
15
+ user.shopify_domain = auth_session.domain
16
+ user.access_scopes = auth_session.access_scopes
17
+
18
+ user.save!
19
+ user.id
20
+ end
21
+
22
+ def retrieve(id)
23
+ user = find_by(id: id)
24
+ construct_session(user)
25
+ end
26
+
27
+ def retrieve_by_shopify_user_id(user_id)
28
+ user = find_by(shopify_user_id: user_id)
29
+ construct_session(user)
30
+ end
31
+
32
+ private
33
+
34
+ def construct_session(user)
35
+ return unless user
36
+
37
+ ShopifyAPI::Session.new(
38
+ domain: user.shopify_domain,
39
+ token: user.shopify_token,
40
+ api_version: user.api_version,
41
+ access_scopes: user.access_scopes
42
+ )
43
+ end
44
+ end
45
+
46
+ def access_scopes=(scopes)
47
+ super(scopes)
48
+ rescue NotImplementedError, NoMethodError
49
+ raise NotImplementedError, "#access_scopes= must be defined to handle storing access scopes: #{scopes}"
50
+ end
51
+
52
+ def access_scopes
53
+ super
54
+ rescue NotImplementedError, NoMethodError
55
+ raise NotImplementedError, "#access_scopes= must be defined to hook into stored access scopes"
56
+ end
57
+ end
58
+ end
@@ -20,5 +20,17 @@ module ShopifyApp
20
20
  rescue ActiveResource::ConnectionError
21
21
  logger.error("[ShopifyAPI::ApiVersion] Unable to fetch api_versions from Shopify")
22
22
  end
23
+
24
+ def self.shop_login_url(shop:, return_to:)
25
+ return ShopifyApp.configuration.login_url unless shop
26
+ url = URI(ShopifyApp.configuration.login_url)
27
+
28
+ url.query = URI.encode_www_form(
29
+ shop: shop,
30
+ return_to: return_to,
31
+ )
32
+
33
+ url.to_s
34
+ end
23
35
  end
24
36
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ShopifyApp
3
- VERSION = '17.0.3'
3
+ VERSION = '17.2.0'
4
4
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shopify_app",
3
- "version": "17.0.3",
3
+ "version": "17.2.0",
4
4
  "repository": "git@github.com:Shopify/shopify_app.git",
5
5
  "author": "Shopify",
6
6
  "license": "MIT",
data/shopify_app.gemspec CHANGED
@@ -14,8 +14,8 @@ Gem::Specification.new do |s|
14
14
  s.metadata['allowed_push_host'] = 'https://rubygems.org'
15
15
 
16
16
  s.add_runtime_dependency('browser_sniffer', '~> 1.2.2')
17
- s.add_runtime_dependency('rails', '> 5.2.1', '< 6.1')
18
- s.add_runtime_dependency('shopify_api', '~> 9.1')
17
+ s.add_runtime_dependency('rails', '> 5.2.1', '< 6.2')
18
+ s.add_runtime_dependency('shopify_api', '~> 9.4')
19
19
  s.add_runtime_dependency('omniauth-shopify-oauth2', '~> 2.2.2')
20
20
  s.add_runtime_dependency('jwt', '~> 2.2.1')
21
21
  s.add_runtime_dependency('redirect_safely', '~> 1.0')
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: 17.0.3
4
+ version: 17.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-22 00:00:00.000000000 Z
11
+ date: 2021-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: browser_sniffer
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: 5.2.1
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: '6.1'
36
+ version: '6.2'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -43,21 +43,21 @@ dependencies:
43
43
  version: 5.2.1
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '6.1'
46
+ version: '6.2'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: shopify_api
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '9.1'
53
+ version: '9.4'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '9.1'
60
+ version: '9.4'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: omniauth-shopify-oauth2
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -248,7 +248,9 @@ extra_rdoc_files: []
248
248
  files:
249
249
  - ".babelrc"
250
250
  - ".github/CODEOWNERS"
251
- - ".github/ISSUE_TEMPLATE.md"
251
+ - ".github/ISSUE_TEMPLATE/bug-report.md"
252
+ - ".github/ISSUE_TEMPLATE/config.yml"
253
+ - ".github/ISSUE_TEMPLATE/feature-request.md"
252
254
  - ".github/PULL_REQUEST_TEMPLATE.md"
253
255
  - ".github/probots.yml"
254
256
  - ".github/workflows/build.yml"
@@ -259,6 +261,7 @@ files:
259
261
  - ".rubocop.yml"
260
262
  - ".ruby-version"
261
263
  - CHANGELOG.md
264
+ - CONTRIBUTING.md
262
265
  - Gemfile
263
266
  - Gemfile.lock
264
267
  - LICENSE
@@ -278,6 +281,7 @@ files:
278
281
  - app/controllers/concerns/shopify_app/authenticated.rb
279
282
  - app/controllers/concerns/shopify_app/ensure_authenticated_links.rb
280
283
  - app/controllers/concerns/shopify_app/require_known_shop.rb
284
+ - app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb
281
285
  - app/controllers/shopify_app/authenticated_controller.rb
282
286
  - app/controllers/shopify_app/callback_controller.rb
283
287
  - app/controllers/shopify_app/extension_verification_controller.rb
@@ -321,8 +325,15 @@ files:
321
325
  - docs/Quickstart.md
322
326
  - docs/Releasing.md
323
327
  - docs/Troubleshooting.md
324
- - docs/install-on-dev-shop.png
325
- - docs/test-your-app.png
328
+ - docs/Upgrading.md
329
+ - docs/shopify_app/authentication.md
330
+ - docs/shopify_app/engine.md
331
+ - docs/shopify_app/generators.md
332
+ - docs/shopify_app/handling-access-scopes-changes.md
333
+ - docs/shopify_app/script-tags.md
334
+ - docs/shopify_app/session-repository.md
335
+ - docs/shopify_app/testing.md
336
+ - docs/shopify_app/webhooks.md
326
337
  - images/app-proxy-screenshot.png
327
338
  - karma.conf.js
328
339
  - lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb
@@ -351,7 +362,7 @@ files:
351
362
  - lib/generators/shopify_app/install/templates/shopify_app.js
352
363
  - lib/generators/shopify_app/install/templates/shopify_app.rb.tt
353
364
  - lib/generators/shopify_app/install/templates/shopify_app_index.js
354
- - lib/generators/shopify_app/install/templates/shopify_provider.rb
365
+ - lib/generators/shopify_app/install/templates/shopify_provider.rb.tt
355
366
  - lib/generators/shopify_app/install/templates/user_agent.rb
356
367
  - lib/generators/shopify_app/products_controller/products_controller_generator.rb
357
368
  - lib/generators/shopify_app/products_controller/templates/products_controller.rb
@@ -361,16 +372,21 @@ files:
361
372
  - lib/generators/shopify_app/routes/routes_generator.rb
362
373
  - lib/generators/shopify_app/routes/templates/routes.rb
363
374
  - lib/generators/shopify_app/shop_model/shop_model_generator.rb
375
+ - lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_scopes_column.erb
364
376
  - lib/generators/shopify_app/shop_model/templates/db/migrate/create_shops.erb
365
377
  - lib/generators/shopify_app/shop_model/templates/shop.rb
366
378
  - lib/generators/shopify_app/shop_model/templates/shops.yml
367
379
  - lib/generators/shopify_app/shopify_app_generator.rb
380
+ - lib/generators/shopify_app/user_model/templates/db/migrate/add_user_access_scopes_column.erb
368
381
  - lib/generators/shopify_app/user_model/templates/db/migrate/create_users.erb
369
382
  - lib/generators/shopify_app/user_model/templates/user.rb
370
383
  - lib/generators/shopify_app/user_model/templates/users.yml
371
384
  - lib/generators/shopify_app/user_model/user_model_generator.rb
372
385
  - lib/generators/shopify_app/views/views_generator.rb
373
386
  - lib/shopify_app.rb
387
+ - lib/shopify_app/access_scopes/noop_strategy.rb
388
+ - lib/shopify_app/access_scopes/shop_strategy.rb
389
+ - lib/shopify_app/access_scopes/user_strategy.rb
374
390
  - lib/shopify_app/configuration.rb
375
391
  - lib/shopify_app/controller_concerns/app_proxy_verification.rb
376
392
  - lib/shopify_app/controller_concerns/csrf_protection.rb
@@ -387,6 +403,7 @@ files:
387
403
  - lib/shopify_app/managers/webhooks_manager.rb
388
404
  - lib/shopify_app/middleware/jwt_middleware.rb
389
405
  - lib/shopify_app/middleware/same_site_cookie_middleware.rb
406
+ - lib/shopify_app/omniauth/omniauth_configuration.rb
390
407
  - lib/shopify_app/session/in_memory_session_store.rb
391
408
  - lib/shopify_app/session/in_memory_shop_session_store.rb
392
409
  - lib/shopify_app/session/in_memory_user_session_store.rb
@@ -395,7 +412,9 @@ files:
395
412
  - lib/shopify_app/session/session_repository.rb
396
413
  - lib/shopify_app/session/session_storage.rb
397
414
  - lib/shopify_app/session/shop_session_storage.rb
415
+ - lib/shopify_app/session/shop_session_storage_with_scopes.rb
398
416
  - lib/shopify_app/session/user_session_storage.rb
417
+ - lib/shopify_app/session/user_session_storage_with_scopes.rb
399
418
  - lib/shopify_app/test_helpers/all.rb
400
419
  - lib/shopify_app/test_helpers/webhook_verification_helper.rb
401
420
  - lib/shopify_app/utils.rb