shopify_app 13.0.0 → 13.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -6
  3. data/CHANGELOG.md +10 -0
  4. data/Gemfile +3 -0
  5. data/README.md +32 -1
  6. data/Rakefile +1 -0
  7. data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
  8. data/app/controllers/shopify_app/callback_controller.rb +1 -1
  9. data/app/controllers/shopify_app/sessions_controller.rb +8 -5
  10. data/app/controllers/shopify_app/webhooks_controller.rb +6 -5
  11. data/config/locales/fi.yml +1 -1
  12. data/config/routes.rb +1 -0
  13. data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +5 -3
  14. data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -0
  15. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +2 -1
  16. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +4 -4
  17. data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +5 -4
  18. data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb +5 -0
  19. data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +4 -3
  20. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +3 -3
  21. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +10 -9
  22. data/lib/generators/shopify_app/controllers/controllers_generator.rb +1 -0
  23. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +4 -3
  24. data/lib/generators/shopify_app/install/install_generator.rb +9 -8
  25. data/lib/generators/shopify_app/install/templates/omniauth.rb +2 -1
  26. data/lib/generators/shopify_app/install/templates/user_agent.rb +2 -1
  27. data/lib/generators/shopify_app/routes/routes_generator.rb +1 -0
  28. data/lib/generators/shopify_app/routes/templates/routes.rb +10 -9
  29. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +5 -4
  30. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -0
  31. data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
  32. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -0
  33. data/lib/generators/shopify_app/user_model/user_model_generator.rb +5 -4
  34. data/lib/generators/shopify_app/views/views_generator.rb +1 -0
  35. data/lib/shopify_app.rb +7 -4
  36. data/lib/shopify_app/configuration.rb +15 -8
  37. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +3 -2
  38. data/lib/shopify_app/controller_concerns/embedded_app.rb +3 -2
  39. data/lib/shopify_app/controller_concerns/localization.rb +1 -0
  40. data/lib/shopify_app/controller_concerns/login_protection.rb +46 -11
  41. data/lib/shopify_app/controller_concerns/webhook_verification.rb +2 -1
  42. data/lib/shopify_app/engine.rb +1 -0
  43. data/lib/shopify_app/jobs/scripttags_manager_job.rb +1 -1
  44. data/lib/shopify_app/jobs/webhooks_manager_job.rb +1 -1
  45. data/lib/shopify_app/managers/scripttags_manager.rb +4 -3
  46. data/lib/shopify_app/managers/webhooks_manager.rb +4 -3
  47. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +2 -1
  48. data/lib/shopify_app/session/in_memory_session_store.rb +7 -3
  49. data/lib/shopify_app/session/in_memory_shop_session_store.rb +10 -0
  50. data/lib/shopify_app/session/in_memory_user_session_store.rb +10 -0
  51. data/lib/shopify_app/session/jwt.rb +48 -0
  52. data/lib/shopify_app/session/null_user_session_store.rb +22 -0
  53. data/lib/shopify_app/session/session_repository.rb +13 -16
  54. data/lib/shopify_app/session/session_storage.rb +1 -0
  55. data/lib/shopify_app/session/shop_session_storage.rb +21 -9
  56. data/lib/shopify_app/session/user_session_storage.rb +19 -8
  57. data/lib/shopify_app/test_helpers/all.rb +1 -0
  58. data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +16 -0
  59. data/lib/shopify_app/utils.rb +6 -5
  60. data/lib/shopify_app/version.rb +2 -1
  61. data/package-lock.json +4 -4
  62. data/package.json +1 -1
  63. data/shopify_app.gemspec +8 -4
  64. data/yarn.lock +3 -3
  65. metadata +22 -3
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -16,7 +17,7 @@ module ShopifyApp
16
17
  @scope = format_array_argument(options['scope'])
17
18
  @api_version = options['api_version'] || ShopifyAPI::Meta.admin_versions.find(&:latest_supported).handle
18
19
 
19
- template 'shopify_app.rb', 'config/initializers/shopify_app.rb'
20
+ template('shopify_app.rb', 'config/initializers/shopify_app.rb')
20
21
  end
21
22
 
22
23
  def create_session_store_initializer
@@ -24,22 +25,22 @@ module ShopifyApp
24
25
  end
25
26
 
26
27
  def create_and_inject_into_omniauth_initializer
27
- unless File.exist? "config/initializers/omniauth.rb"
28
- copy_file 'omniauth.rb', 'config/initializers/omniauth.rb'
28
+ unless File.exist?("config/initializers/omniauth.rb")
29
+ copy_file('omniauth.rb', 'config/initializers/omniauth.rb')
29
30
  end
30
31
 
31
32
  inject_into_file(
32
33
  'config/initializers/omniauth.rb',
33
34
  File.read(File.expand_path(find_in_source_paths('shopify_provider.rb'))),
34
- after: "Rails.application.config.middleware.use OmniAuth::Builder do\n"
35
+ after: "Rails.application.config.middleware.use(OmniAuth::Builder) do\n"
35
36
  )
36
37
  end
37
38
 
38
39
  def create_embedded_app_layout
39
40
  return unless embedded_app?
40
41
 
41
- copy_file 'embedded_app.html.erb', 'app/views/layouts/embedded_app.html.erb'
42
- copy_file '_flash_messages.html.erb', 'app/views/layouts/_flash_messages.html.erb'
42
+ copy_file('embedded_app.html.erb', 'app/views/layouts/embedded_app.html.erb')
43
+ copy_file('_flash_messages.html.erb', 'app/views/layouts/_flash_messages.html.erb')
43
44
 
44
45
  if ShopifyApp.use_webpacker?
45
46
  copy_file('shopify_app.js', 'app/javascript/shopify_app/shopify_app.js')
@@ -53,11 +54,11 @@ module ShopifyApp
53
54
  end
54
55
 
55
56
  def create_user_agent_initializer
56
- template 'user_agent.rb', 'config/initializers/user_agent.rb'
57
+ template('user_agent.rb', 'config/initializers/user_agent.rb')
57
58
  end
58
59
 
59
60
  def mount_engine
60
- route "mount ShopifyApp::Engine, at: '/'"
61
+ route("mount ShopifyApp::Engine, at: '/'")
61
62
  end
62
63
 
63
64
  def insert_hosts_into_development_config
@@ -1,2 +1,3 @@
1
- Rails.application.config.middleware.use OmniAuth::Builder do
1
+ # frozen_string_literal: true
2
+ Rails.application.config.middleware.use(OmniAuth::Builder) do
2
3
  end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyAPI
2
3
  class Base < ActiveResource::Base
3
- self.headers['User-Agent'] << " | ShopifyApp/#{ShopifyApp::VERSION}"
4
+ headers['User-Agent'] << " | ShopifyApp/#{ShopifyApp::VERSION}"
4
5
  end
5
6
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
1
2
 
2
- controller :sessions do
3
- get 'login' => :new, :as => :login
4
- post 'login' => :create, :as => :authenticate
5
- get 'auth/shopify/callback' => :callback
6
- get 'logout' => :destroy, :as => :logout
7
- end
3
+ controller :sessions do
4
+ get 'login' => :new, :as => :login
5
+ post 'login' => :create, :as => :authenticate
6
+ get 'auth/shopify/callback' => :callback
7
+ get 'logout' => :destroy, :as => :logout
8
+ end
8
9
 
9
- namespace :webhooks do
10
- post ':type' => :receive
11
- end
10
+ namespace :webhooks do
11
+ post ':type' => :receive
12
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
  require 'rails/generators/active_record'
3
4
 
@@ -8,19 +9,19 @@ module ShopifyApp
8
9
  source_root File.expand_path('../templates', __FILE__)
9
10
 
10
11
  def create_shop_model
11
- copy_file 'shop.rb', 'app/models/shop.rb'
12
+ copy_file('shop.rb', 'app/models/shop.rb')
12
13
  end
13
14
 
14
15
  def create_shop_migration
15
- migration_template 'db/migrate/create_shops.erb', 'db/migrate/create_shops.rb'
16
+ migration_template('db/migrate/create_shops.erb', 'db/migrate/create_shops.rb')
16
17
  end
17
18
 
18
19
  def update_shopify_app_initializer
19
- gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryShopSessionStore', 'Shop'
20
+ gsub_file('config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryShopSessionStore', 'Shop')
20
21
  end
21
22
 
22
23
  def create_shop_fixtures
23
- copy_file 'shops.yml', 'test/fixtures/shops.yml'
24
+ copy_file('shops.yml', 'test/fixtures/shops.yml')
24
25
  end
25
26
 
26
27
  private
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class Shop < ActiveRecord::Base
2
3
  include ShopifyApp::ShopSessionStorage
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  module Generators
3
4
  class ShopifyAppGenerator < Rails::Generators::Base
@@ -7,10 +8,10 @@ module ShopifyApp
7
8
  end
8
9
 
9
10
  def run_all_generators
10
- generate "shopify_app:install #{@opts.join(' ')}"
11
- generate "shopify_app:shop_model"
11
+ generate("shopify_app:install #{@opts.join(' ')}")
12
+ generate("shopify_app:shop_model")
12
13
  generate("shopify_app:authenticated_controller")
13
- generate "shopify_app:home_controller"
14
+ generate("shopify_app:home_controller")
14
15
  end
15
16
  end
16
17
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class User < ActiveRecord::Base
2
3
  include ShopifyApp::UserSessionStorage
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
  require 'rails/generators/active_record'
3
4
 
@@ -8,19 +9,19 @@ module ShopifyApp
8
9
  source_root File.expand_path('../templates', __FILE__)
9
10
 
10
11
  def create_user_model
11
- copy_file 'user.rb', 'app/models/user.rb'
12
+ copy_file('user.rb', 'app/models/user.rb')
12
13
  end
13
14
 
14
15
  def create_user_migration
15
- migration_template 'db/migrate/create_users.erb', 'db/migrate/create_users.rb'
16
+ migration_template('db/migrate/create_users.erb', 'db/migrate/create_users.rb')
16
17
  end
17
18
 
18
19
  def update_shopify_app_initializer
19
- gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryUserSessionStore', 'User'
20
+ gsub_file('config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryUserSessionStore', 'User')
20
21
  end
21
22
 
22
23
  def create_user_fixtures
23
- copy_file 'users.yml', 'test/fixtures/users.yml'
24
+ copy_file('users.yml', 'test/fixtures/users.yml')
24
25
  end
25
26
 
26
27
  private
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'shopify_app/version'
2
3
 
3
4
  # deps
@@ -44,11 +45,13 @@ module ShopifyApp
44
45
  require 'shopify_app/middleware/same_site_cookie_middleware'
45
46
 
46
47
  # session
47
- require 'shopify_app/session/session_storage'
48
- require 'shopify_app/session/shop_session_storage'
49
- require 'shopify_app/session/user_session_storage'
50
- require 'shopify_app/session/session_repository'
51
48
  require 'shopify_app/session/in_memory_session_store'
52
49
  require 'shopify_app/session/in_memory_shop_session_store'
53
50
  require 'shopify_app/session/in_memory_user_session_store'
51
+ require 'shopify_app/session/jwt'
52
+ require 'shopify_app/session/null_user_session_store'
53
+ require 'shopify_app/session/session_repository'
54
+ require 'shopify_app/session/session_storage'
55
+ require 'shopify_app/session/shop_session_storage'
56
+ require 'shopify_app/session/user_session_storage'
54
57
  end
@@ -1,11 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  class Configuration
3
-
4
4
  # Shopify App settings. These values should match the configuration
5
5
  # for the app in your Shopify Partners page. Change your settings in
6
6
  # `config/initializers/shopify_app.rb`
7
7
  attr_accessor :application_name
8
- attr_accessor :api_key
8
+ attr_accessor :api_key
9
9
  attr_accessor :secret
10
10
  attr_accessor :old_secret
11
11
  attr_accessor :scope
@@ -14,13 +14,11 @@ module ShopifyApp
14
14
  attr_accessor :webhooks
15
15
  attr_accessor :scripttags
16
16
  attr_accessor :after_authenticate_job
17
- attr_reader :shop_session_repository
18
- attr_reader :user_session_repository
19
17
  attr_accessor :api_version
20
18
 
21
19
  # customise urls
22
20
  attr_accessor :root_url
23
- attr_accessor :login_url
21
+ attr_writer :login_url
24
22
 
25
23
  # customise ActiveJob queue names
26
24
  attr_accessor :scripttags_manager_queue_name
@@ -36,7 +34,10 @@ module ShopifyApp
36
34
  attr_accessor :webhook_jobs_namespace
37
35
 
38
36
  # allow enabling of same site none on cookies
39
- attr_accessor :enable_same_site_none
37
+ attr_writer :enable_same_site_none
38
+
39
+ # allow enabling jwt headers for authentication
40
+ attr_accessor :allow_jwt_authentication
40
41
 
41
42
  def initialize
42
43
  @root_url = '/'
@@ -51,15 +52,21 @@ module ShopifyApp
51
52
  end
52
53
 
53
54
  def user_session_repository=(klass)
54
- @user_session_repository = klass
55
55
  ShopifyApp::SessionRepository.user_storage = klass
56
56
  end
57
57
 
58
+ def user_session_repository
59
+ ShopifyApp::SessionRepository.user_storage
60
+ end
61
+
58
62
  def shop_session_repository=(klass)
59
- @shop_session_repository = klass
60
63
  ShopifyApp::SessionRepository.shop_storage = klass
61
64
  end
62
65
 
66
+ def shop_session_repository
67
+ ShopifyApp::SessionRepository.shop_storage
68
+ end
69
+
63
70
  def has_webhooks?
64
71
  webhooks.present?
65
72
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  module AppProxyVerification
3
4
  extend ActiveSupport::Concern
@@ -8,7 +9,7 @@ module ShopifyApp
8
9
  end
9
10
 
10
11
  def verify_proxy_request
11
- return head :forbidden unless query_string_valid?(request.query_string)
12
+ return head(:forbidden) unless query_string_valid?(request.query_string)
12
13
  end
13
14
 
14
15
  private
@@ -26,7 +27,7 @@ module ShopifyApp
26
27
  end
27
28
 
28
29
  def calculated_signature(query_hash_without_signature)
29
- sorted_params = query_hash_without_signature.collect{|k,v| "#{k}=#{Array(v).join(',')}"}.sort.join
30
+ sorted_params = query_hash_without_signature.collect { |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join
30
31
 
31
32
  OpenSSL::HMAC.hexdigest(
32
33
  OpenSSL::Digest.new('sha256'),
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  module EmbeddedApp
3
4
  extend ActiveSupport::Concern
4
5
 
5
6
  included do
6
7
  if ShopifyApp.configuration.embedded_app?
7
- after_action :set_esdk_headers
8
- layout 'embedded_app'
8
+ after_action(:set_esdk_headers)
9
+ layout('embedded_app')
9
10
  end
10
11
  end
11
12
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  module Localization
3
4
  extend ActiveSupport::Concern
@@ -11,7 +11,7 @@ module ShopifyApp
11
11
 
12
12
  included do
13
13
  after_action :set_test_cookie
14
- rescue_from ActiveResource::UnauthorizedAccess, :with => :close_session
14
+ rescue_from ActiveResource::UnauthorizedAccess, with: :close_session
15
15
  end
16
16
 
17
17
  def activate_shopify_session
@@ -27,20 +27,38 @@ module ShopifyApp
27
27
  end
28
28
 
29
29
  def current_shopify_session
30
- if session[:user_id].present?
31
- @current_shopify_session ||= user_session
32
- else
33
- @current_shopify_session ||= shop_session
30
+ @current_shopify_session ||= begin
31
+ user_session || shop_session
34
32
  end
35
33
  end
36
34
 
37
35
  def user_session
38
- return if session[:user_id].blank?
36
+ user_session_by_jwt || user_session_by_cookie
37
+ end
38
+
39
+ def user_session_by_jwt
40
+ return unless ShopifyApp.configuration.allow_jwt_authentication
41
+ return unless jwt_shopify_user_id
42
+ ShopifyApp::SessionRepository.retrieve_user_session_by_shopify_user_id(jwt_shopify_user_id)
43
+ end
44
+
45
+ def user_session_by_cookie
46
+ return unless session[:user_id].present?
39
47
  ShopifyApp::SessionRepository.retrieve_user_session(session[:user_id])
40
48
  end
41
49
 
42
50
  def shop_session
43
- return if session[:shop_id].blank?
51
+ shop_session_by_jwt || shop_session_by_cookie
52
+ end
53
+
54
+ def shop_session_by_jwt
55
+ return unless ShopifyApp.configuration.allow_jwt_authentication
56
+ return unless jwt_shopify_domain
57
+ ShopifyApp::SessionRepository.retrieve_shop_session_by_shopify_domain(jwt_shopify_domain)
58
+ end
59
+
60
+ def shop_session_by_cookie
61
+ return unless session[:shop_id].present?
44
62
  ShopifyApp::SessionRepository.retrieve_shop_session(session[:shop_id])
45
63
  end
46
64
 
@@ -50,7 +68,9 @@ module ShopifyApp
50
68
 
51
69
  end
52
70
 
53
- if current_shopify_session && params[:shop] && params[:shop].is_a?(String) && (current_shopify_session.domain != params[:shop])
71
+ if current_shopify_session &&
72
+ params[:shop] && params[:shop].is_a?(String) &&
73
+ (current_shopify_session.domain != params[:shop])
54
74
  clear_session = true
55
75
  end
56
76
 
@@ -62,9 +82,23 @@ module ShopifyApp
62
82
 
63
83
  protected
64
84
 
85
+ def jwt_shopify_domain
86
+ return unless jwt
87
+ @jwt_shopify_domain ||= JWT.new(jwt).shopify_domain
88
+ end
89
+
90
+ def jwt_shopify_user_id
91
+ return unless jwt
92
+ @jwt_user_id ||= JWT.new(jwt).shopify_user_id
93
+ end
94
+
95
+ def jwt
96
+ @jwt ||= authenticate_with_http_token { |token| token }
97
+ end
98
+
65
99
  def redirect_to_login
66
100
  if request.xhr?
67
- head :unauthorized
101
+ head(:unauthorized)
68
102
  else
69
103
  if request.get?
70
104
  path = request.path
@@ -128,9 +162,10 @@ module ShopifyApp
128
162
 
129
163
  def fullpage_redirect_to(url)
130
164
  if ShopifyApp.configuration.embedded_app?
131
- render 'shopify_app/shared/redirect', layout: false, locals: { url: url, current_shopify_domain: current_shopify_domain }
165
+ render('shopify_app/shared/redirect', layout: false,
166
+ locals: { url: url, current_shopify_domain: current_shopify_domain })
132
167
  else
133
- redirect_to url
168
+ redirect_to(url)
134
169
  end
135
170
  end
136
171
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  module WebhookVerification
3
4
  extend ActiveSupport::Concern
@@ -11,7 +12,7 @@ module ShopifyApp
11
12
 
12
13
  def verify_request
13
14
  data = request.raw_post
14
- return head :unauthorized unless hmac_valid?(data)
15
+ return head(:unauthorized) unless hmac_valid?(data)
15
16
  end
16
17
 
17
18
  def hmac_valid?(data)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  class Engine < Rails::Engine
3
4
  engine_name 'shopify_app'
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  class ScripttagsManagerJob < ActiveJob::Base
3
-
4
4
  queue_as do
5
5
  ShopifyApp.configuration.scripttags_manager_queue_name
6
6
  end