shopify_app 13.0.0 → 13.0.1

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