shopify_app 12.0.0 → 13.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/README.md +67 -20
- data/app/assets/javascripts/shopify_app/itp_helper.js +6 -6
- data/app/assets/javascripts/shopify_app/storage_access.js +35 -6
- data/app/controllers/concerns/shopify_app/authenticated.rb +1 -1
- data/app/controllers/shopify_app/callback_controller.rb +14 -10
- data/app/controllers/shopify_app/sessions_controller.rb +49 -13
- data/app/views/shopify_app/sessions/enable_cookies.html.erb +1 -1
- data/app/views/shopify_app/sessions/request_storage_access.html.erb +1 -1
- data/config/locales/pt-BR.yml +1 -1
- data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +1 -5
- data/lib/generators/shopify_app/home_controller/templates/index.html.erb +1 -1
- data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
- data/lib/generators/shopify_app/install/templates/shopify_app.rb +1 -1
- data/lib/generators/shopify_app/install/templates/shopify_provider.rb +1 -1
- data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +1 -1
- data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -1
- data/lib/generators/shopify_app/user_model/templates/user.rb +1 -1
- data/lib/generators/shopify_app/user_model/user_model_generator.rb +1 -1
- data/lib/shopify_app/configuration.rb +12 -9
- data/lib/shopify_app/controller_concerns/login_protection.rb +39 -22
- data/lib/shopify_app/engine.rb +1 -1
- data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +18 -45
- data/lib/shopify_app/session/in_memory_shop_session_store.rb +4 -0
- data/lib/shopify_app/session/in_memory_user_session_store.rb +4 -0
- data/lib/shopify_app/session/session_repository.rb +38 -13
- data/lib/shopify_app/session/session_storage.rb +0 -10
- data/lib/shopify_app/session/{storage_strategies/shop_storage_strategy.rb → shop_session_storage.rb} +9 -2
- data/lib/shopify_app/session/{storage_strategies/user_storage_strategy.rb → user_session_storage.rb} +10 -3
- data/lib/shopify_app/version.rb +1 -1
- data/lib/shopify_app.rb +4 -2
- data/package-lock.json +1228 -1207
- data/package.json +2 -2
- data/shopify_app.gemspec +4 -4
- metadata +12 -10
@@ -32,7 +32,7 @@
|
|
32
32
|
myshopifyUrl: "https://#{current_shopify_domain}",
|
33
33
|
hasStorageAccessUrl: "#{has_storage_access_url}",
|
34
34
|
doesNotHaveStorageAccessUrl: "#{does_not_have_storage_access_url}",
|
35
|
-
|
35
|
+
appTargetUrl: "#{app_target_url}"
|
36
36
|
},
|
37
37
|
},
|
38
38
|
)
|
@@ -24,7 +24,7 @@
|
|
24
24
|
myshopifyUrl: "https://#{current_shopify_domain}",
|
25
25
|
hasStorageAccessUrl: "#{has_storage_access_url}",
|
26
26
|
doesNotHaveStorageAccessUrl: "#{does_not_have_storage_access_url}",
|
27
|
-
|
27
|
+
appTargetUrl: "#{app_target_url}"
|
28
28
|
},
|
29
29
|
},
|
30
30
|
)
|
data/config/locales/pt-BR.yml
CHANGED
@@ -4,7 +4,7 @@ pt-BR:
|
|
4
4
|
could_not_log_in: Não foi possível fazer login na Shopify store
|
5
5
|
invalid_shop_url: Domínio de loja inválido
|
6
6
|
enable_cookies_heading: Habilitar cookies de %{app}
|
7
|
-
enable_cookies_body: Você
|
7
|
+
enable_cookies_body: Você precisa habilitar manualmente os cookies neste navegador
|
8
8
|
para usar %{app} dentro da Shopify.
|
9
9
|
enable_cookies_footer: Os cookies permitem que o app o autentique armazenando temporariamente
|
10
10
|
suas preferências e dados pessoais. Eles expiram depois de 30 dias.
|
@@ -3,11 +3,7 @@
|
|
3
3
|
class MarketingActivitiesController < ShopifyApp::ExtensionVerificationController
|
4
4
|
def preload_form_data
|
5
5
|
preload_data = {
|
6
|
-
"form_data": {
|
7
|
-
"budget": {
|
8
|
-
"currency": "USD",
|
9
|
-
}
|
10
|
-
}
|
6
|
+
"form_data": {}
|
11
7
|
}
|
12
8
|
render(json: preload_data, status: :ok)
|
13
9
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<ul>
|
4
4
|
<% @products.each do |product| %>
|
5
|
-
<li><%= link_to product.title, "https://#{@
|
5
|
+
<li><%= link_to product.title, "https://#{@current_shopify_session.domain}/admin/products/#{product.id}", target: "_top" %></li>
|
6
6
|
<% end %>
|
7
7
|
</ul>
|
8
8
|
|
@@ -28,7 +28,7 @@
|
|
28
28
|
|
29
29
|
<%= content_tag(:div, nil, id: 'shopify-app-init', data: {
|
30
30
|
api_key: ShopifyApp.configuration.api_key,
|
31
|
-
shop_origin: (@
|
31
|
+
shop_origin: (@current_shopify_session.domain if @current_shopify_session),
|
32
32
|
debug: Rails.env.development?
|
33
33
|
} ) %>
|
34
34
|
|
@@ -8,7 +8,7 @@ ShopifyApp.configure do |config|
|
|
8
8
|
config.embedded_app = <%= embedded_app? %>
|
9
9
|
config.after_authenticate_job = false
|
10
10
|
config.api_version = "<%= @api_version %>"
|
11
|
-
config.
|
11
|
+
config.shop_session_repository = 'ShopifyApp::InMemoryShopSessionStore'
|
12
12
|
end
|
13
13
|
|
14
14
|
# ShopifyApp::Utils.fetch_known_api_versions # Uncomment to fetch known api versions from shopify servers on boot
|
@@ -4,7 +4,6 @@ provider :shopify,
|
|
4
4
|
ShopifyApp.configuration.api_key,
|
5
5
|
ShopifyApp.configuration.secret,
|
6
6
|
scope: ShopifyApp.configuration.scope,
|
7
|
-
per_user_permissions: ShopifyApp.configuration.per_user_tokens,
|
8
7
|
setup: lambda { |env|
|
9
8
|
strategy = env['omniauth.strategy']
|
10
9
|
|
@@ -17,4 +16,5 @@ provider :shopify,
|
|
17
16
|
|
18
17
|
strategy.options[:client_options][:site] = shop
|
19
18
|
strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
|
19
|
+
strategy.options[:per_user_permissions] = strategy.session[:user_tokens]
|
20
20
|
}
|
@@ -16,7 +16,7 @@ module ShopifyApp
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def update_shopify_app_initializer
|
19
|
-
gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::
|
19
|
+
gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryShopSessionStore', 'Shop'
|
20
20
|
end
|
21
21
|
|
22
22
|
def create_shop_fixtures
|
@@ -16,7 +16,7 @@ module ShopifyApp
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def update_shopify_app_initializer
|
19
|
-
gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::
|
19
|
+
gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryUserSessionStore', 'User'
|
20
20
|
end
|
21
21
|
|
22
22
|
def create_user_fixtures
|
@@ -5,7 +5,7 @@ module ShopifyApp
|
|
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
|
8
|
+
attr_accessor :api_key
|
9
9
|
attr_accessor :secret
|
10
10
|
attr_accessor :old_secret
|
11
11
|
attr_accessor :scope
|
@@ -14,9 +14,8 @@ module ShopifyApp
|
|
14
14
|
attr_accessor :webhooks
|
15
15
|
attr_accessor :scripttags
|
16
16
|
attr_accessor :after_authenticate_job
|
17
|
-
attr_reader :
|
18
|
-
|
19
|
-
alias_method :per_user_tokens?, :per_user_tokens
|
17
|
+
attr_reader :shop_session_repository
|
18
|
+
attr_reader :user_session_repository
|
20
19
|
attr_accessor :api_version
|
21
20
|
|
22
21
|
# customise urls
|
@@ -44,7 +43,6 @@ module ShopifyApp
|
|
44
43
|
@myshopify_domain = 'myshopify.com'
|
45
44
|
@scripttags_manager_queue_name = Rails.application.config.active_job.queue_name
|
46
45
|
@webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
|
47
|
-
@per_user_tokens = false
|
48
46
|
@disable_webpacker = ENV['SHOPIFY_APP_DISABLE_WEBPACKER'].present?
|
49
47
|
end
|
50
48
|
|
@@ -52,9 +50,14 @@ module ShopifyApp
|
|
52
50
|
@login_url || File.join(@root_url, 'login')
|
53
51
|
end
|
54
52
|
|
55
|
-
def
|
56
|
-
@
|
57
|
-
ShopifyApp::SessionRepository.
|
53
|
+
def user_session_repository=(klass)
|
54
|
+
@user_session_repository = klass
|
55
|
+
ShopifyApp::SessionRepository.user_storage = klass
|
56
|
+
end
|
57
|
+
|
58
|
+
def shop_session_repository=(klass)
|
59
|
+
@shop_session_repository = klass
|
60
|
+
ShopifyApp::SessionRepository.shop_storage = klass
|
58
61
|
end
|
59
62
|
|
60
63
|
def has_webhooks?
|
@@ -66,7 +69,7 @@ module ShopifyApp
|
|
66
69
|
end
|
67
70
|
|
68
71
|
def enable_same_site_none
|
69
|
-
@enable_same_site_none.nil? ? embedded_app? : @enable_same_site_none
|
72
|
+
!Rails.env.test? && (@enable_same_site_none.nil? ? embedded_app? : @enable_same_site_none)
|
70
73
|
end
|
71
74
|
end
|
72
75
|
|
@@ -14,44 +14,48 @@ module ShopifyApp
|
|
14
14
|
rescue_from ActiveResource::UnauthorizedAccess, :with => :close_session
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
return redirect_to_login
|
17
|
+
def activate_shopify_session
|
18
|
+
return redirect_to_login if current_shopify_session.blank?
|
19
19
|
clear_top_level_oauth_cookie
|
20
20
|
|
21
21
|
begin
|
22
|
-
ShopifyAPI::Base.activate_session(
|
22
|
+
ShopifyAPI::Base.activate_session(current_shopify_session)
|
23
23
|
yield
|
24
24
|
ensure
|
25
25
|
ShopifyAPI::Base.clear_session
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
30
|
-
if
|
31
|
-
|
32
|
-
@shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify_user]['id'])
|
29
|
+
def current_shopify_session
|
30
|
+
if session[:user_id].present?
|
31
|
+
@current_shopify_session ||= user_session
|
33
32
|
else
|
34
|
-
|
35
|
-
@shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify])
|
33
|
+
@current_shopify_session ||= shop_session
|
36
34
|
end
|
37
35
|
end
|
38
36
|
|
37
|
+
def user_session
|
38
|
+
return if session[:user_id].blank?
|
39
|
+
ShopifyApp::SessionRepository.retrieve_user_session(session[:user_id])
|
40
|
+
end
|
41
|
+
|
42
|
+
def shop_session
|
43
|
+
return if session[:shop_id].blank?
|
44
|
+
ShopifyApp::SessionRepository.retrieve_shop_session(session[:shop_id])
|
45
|
+
end
|
46
|
+
|
39
47
|
def login_again_if_different_user_or_shop
|
40
|
-
if
|
41
|
-
|
42
|
-
sessions_do_not_match = session[:user_session] != params[:session] # current user is different from stored user
|
48
|
+
if session[:user_session].present? && params[:session].present? # session data was sent/stored correctly
|
49
|
+
clear_session = session[:user_session] != params[:session] # current user is different from stored user
|
43
50
|
|
44
|
-
if valid_session_data && sessions_do_not_match
|
45
|
-
clear_session = true
|
46
|
-
end
|
47
51
|
end
|
48
52
|
|
49
|
-
if
|
53
|
+
if current_shopify_session && params[:shop] && params[:shop].is_a?(String) && (current_shopify_session.domain != params[:shop])
|
50
54
|
clear_session = true
|
51
55
|
end
|
52
56
|
|
53
57
|
if clear_session
|
54
|
-
|
58
|
+
clear_shopify_session
|
55
59
|
redirect_to_login
|
56
60
|
end
|
57
61
|
end
|
@@ -76,12 +80,13 @@ module ShopifyApp
|
|
76
80
|
end
|
77
81
|
|
78
82
|
def close_session
|
79
|
-
|
83
|
+
clear_shopify_session
|
80
84
|
redirect_to(login_url_with_optional_shop)
|
81
85
|
end
|
82
86
|
|
83
|
-
def
|
84
|
-
session[:
|
87
|
+
def clear_shopify_session
|
88
|
+
session[:shop_id] = nil
|
89
|
+
session[:user_id] = nil
|
85
90
|
session[:shopify_domain] = nil
|
86
91
|
session[:shopify_user] = nil
|
87
92
|
session[:user_session] = nil
|
@@ -100,8 +105,10 @@ module ShopifyApp
|
|
100
105
|
query_params = {}
|
101
106
|
query_params[:shop] = sanitized_params[:shop] if params[:shop].present?
|
102
107
|
|
103
|
-
|
104
|
-
|
108
|
+
return_to = session[:return_to] || params[:return_to]
|
109
|
+
|
110
|
+
if return_to.present? && return_to_param_required?
|
111
|
+
query_params[:return_to] = return_to
|
105
112
|
end
|
106
113
|
|
107
114
|
has_referer_shop_name = referer_sanitized_shop_name.present?
|
@@ -165,5 +172,15 @@ module ShopifyApp
|
|
165
172
|
def return_address
|
166
173
|
session.delete(:return_to) || ShopifyApp.configuration.root_url
|
167
174
|
end
|
175
|
+
|
176
|
+
def return_address_with_params(params)
|
177
|
+
uri = URI(return_address)
|
178
|
+
uri.query = CGI.parse(uri.query.to_s)
|
179
|
+
.symbolize_keys
|
180
|
+
.transform_values { |v| v.one? ? v.first : v }
|
181
|
+
.merge(params)
|
182
|
+
.to_query
|
183
|
+
uri.to_s
|
184
|
+
end
|
168
185
|
end
|
169
186
|
end
|
data/lib/shopify_app/engine.rb
CHANGED
@@ -14,7 +14,7 @@ module ShopifyApp
|
|
14
14
|
end
|
15
15
|
|
16
16
|
initializer "shopify_app.middleware" do |app|
|
17
|
-
app.config.middleware.
|
17
|
+
app.config.middleware.insert_after(::Rack::Runtime, ShopifyApp::SameSiteCookieMiddleware)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -1,60 +1,33 @@
|
|
1
1
|
module ShopifyApp
|
2
2
|
class SameSiteCookieMiddleware
|
3
|
+
COOKIE_SEPARATOR = "\n"
|
4
|
+
|
3
5
|
def initialize(app)
|
4
6
|
@app = app
|
5
7
|
end
|
6
8
|
|
7
9
|
def call(env)
|
8
|
-
|
9
|
-
ensure
|
10
|
+
status, headers, body = @app.call(env)
|
10
11
|
user_agent = env['HTTP_USER_AGENT']
|
11
12
|
|
12
|
-
if headers && headers['Set-Cookie'] &&
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
if headers && headers['Set-Cookie'] &&
|
14
|
+
BrowserSniffer.new(user_agent).same_site_none_compatible? &&
|
15
|
+
ShopifyApp.configuration.enable_same_site_none &&
|
16
|
+
Rack::Request.new(env).ssl?
|
17
|
+
|
18
|
+
set_cookies = headers['Set-Cookie']
|
19
|
+
.split(COOKIE_SEPARATOR)
|
20
|
+
.compact
|
21
|
+
.map do |cookie|
|
22
|
+
cookie << '; Secure' if not cookie =~ /;\s*secure/i
|
23
|
+
cookie << '; SameSite=None' unless cookie =~ /;\s*samesite=/i
|
24
|
+
cookie
|
20
25
|
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
26
|
|
25
|
-
|
26
|
-
|
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
|
27
|
+
headers['Set-Cookie'] = set_cookies.join(COOKIE_SEPARATOR)
|
28
|
+
end
|
54
29
|
|
55
|
-
|
56
|
-
return digits[1] > minor if digits[1] != minor
|
57
|
-
digits[2] >= build
|
30
|
+
[status, headers, body]
|
58
31
|
end
|
59
32
|
end
|
60
33
|
end
|
@@ -3,31 +3,56 @@ module ShopifyApp
|
|
3
3
|
class ConfigurationError < StandardError; end
|
4
4
|
|
5
5
|
class << self
|
6
|
-
def
|
7
|
-
@
|
6
|
+
def shop_storage=(storage)
|
7
|
+
@shop_storage = storage
|
8
8
|
|
9
|
-
unless storage.nil? || self.
|
10
|
-
raise ArgumentError, "storage must respond to :store and :retrieve"
|
9
|
+
unless storage.nil? || self.shop_storage.respond_to?(:store) && self.shop_storage.respond_to?(:retrieve)
|
10
|
+
raise ArgumentError, "shop storage must respond to :store and :retrieve"
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
storage
|
14
|
+
def user_storage=(storage)
|
15
|
+
@user_storage = storage
|
16
|
+
|
17
|
+
unless storage.nil? || self.user_storage.respond_to?(:store) && self.user_storage.respond_to?(:retrieve)
|
18
|
+
raise ArgumentError, "user storage must respond to :store and :retrieve"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def retrieve_shop_session(id)
|
23
|
+
shop_storage.retrieve(id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def retrieve_user_session(id)
|
27
|
+
user_storage.retrieve(id)
|
16
28
|
end
|
17
29
|
|
18
|
-
def
|
19
|
-
|
30
|
+
def store_shop_session(session)
|
31
|
+
shop_storage.store(session)
|
20
32
|
end
|
21
33
|
|
22
|
-
def
|
23
|
-
|
34
|
+
def store_user_session(session, user)
|
35
|
+
user_storage.store(session, user)
|
36
|
+
end
|
37
|
+
|
38
|
+
def shop_storage
|
39
|
+
load_shop_storage || raise(ConfigurationError.new("ShopifySessionRepository.shop_storage is not configured!"))
|
40
|
+
end
|
41
|
+
|
42
|
+
def user_storage
|
43
|
+
load_user_storage
|
24
44
|
end
|
25
45
|
|
26
46
|
private
|
27
47
|
|
28
|
-
def
|
29
|
-
return unless @
|
30
|
-
@
|
48
|
+
def load_shop_storage
|
49
|
+
return unless @shop_storage
|
50
|
+
@shop_storage.respond_to?(:safe_constantize) ? @shop_storage.safe_constantize : @shop_storage
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_user_storage
|
54
|
+
return unless @user_storage
|
55
|
+
@user_storage.respond_to?(:safe_constantize) ? @user_storage.safe_constantize : @user_storage
|
31
56
|
end
|
32
57
|
end
|
33
58
|
end
|