shopify_app 8.4.2 → 8.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28cc5c500b3efa9576e3808c7dbec511d7269631ed6b5b5fa7d02c6aae1aa854
4
- data.tar.gz: d064aee1e329a10d558890ca8444793e3785783f061ce2ad8e9c9327c9948826
3
+ metadata.gz: 8fe2949f5e38db4532dbcab3c05f96e228e6285fdbb4b729ffefecd8e40b2aad
4
+ data.tar.gz: 2fefc3ce45bc58cd82c874d5217f6abfce0a70ba56cb9105084dbec011f033cb
5
5
  SHA512:
6
- metadata.gz: 87b40ecda005295ecbd2237c8d9e460b935122f961c6e914709de79fa733a34452dcfb71f302b8e58f9b79cc7b076f9fc29f7be58daace8fcaa4d33a42c5b5fa
7
- data.tar.gz: 833a0937d92fd51eef51419848e3067eef2c2f8fc48076111134c4d80e0c47ca448c4123a069a08550c6231850e3d1c588a372cdb32910111f4ae1dc9276625b
6
+ metadata.gz: 1a435b2541e5198fad47a247a97b2a68ce6e741b2b29a4b0b7f2cc29d256fc382a7fd8a56801e982744258e9efdfd3879a13dd4dffc033c88d432ce44d525e44
7
+ data.tar.gz: 1df140b61ca82e090eba2fcabac5f079cc5d87e7cb64bab657cbc725ff1063360fbd88822d4ed76c9224d2fd4e9429ba197dd3c7f6a6312b3c8dbdb060c35f26
data/.gitignore CHANGED
@@ -12,3 +12,5 @@ test/tmp/*
12
12
  # ignore sprockets cache
13
13
  /test/dummy/tmp/*
14
14
  /node_modules/
15
+ .byebug_history
16
+
data/.rubocop.yml CHANGED
@@ -1,3 +1,6 @@
1
+ inherit_from:
2
+ - https://shopify.github.io/ruby-style-guide/rubocop.yml
3
+
1
4
  LineLength:
2
5
  Exclude:
3
6
  - test/**/*
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ 8.5.0
2
+ -----
3
+ Added support for rotating Shopify access tokens:
4
+
5
+ * Added a generator shopify_app:rotate_shopify_token_job for generating the job to perform token rotation
6
+ * Extend Shopify app configuration to support a new and old secret token
7
+ * Extended webhook validation code to support validating against new and old secret tokens
8
+ * See the README for more details: https://github.com/Shopify/shopify_app#rotateshopifytokenjob
9
+
1
10
  8.4.2
2
11
  -----
3
12
  * Clear stale user session during auth callback
data/README.md CHANGED
@@ -364,6 +364,32 @@ We've also provided a generator which creates a skeleton job and updates the ini
364
364
  bin/rails g shopify_app:add_after_authenticate_job
365
365
  ```
366
366
 
367
+ RotateShopifyTokenJob
368
+ ---------------------
369
+
370
+ If your Shopify secret key is leaked, you can use the RotateShopifyTokenJob to perform [API Credential Rotation](https://help.shopify.com/en/api/getting-started/authentication/oauth/api-credential-rotation).
371
+
372
+ Before running the job, you'll need to generate a new secret key from your Shopify Partner dashboard, and update the `/config/initializers/shopify_app.rb` to hold your new and old secret keys:
373
+
374
+ ```ruby
375
+ config.secret = Rails.application.secrets.shopify_secret
376
+ config.old_secret = Rails.application.secrets.old_shopify_secret
377
+ ```
378
+
379
+ We've provided a generator which creates the job and an example rake task:
380
+
381
+ ```sh
382
+ bin/rails g shopify_app:rotate_shopify_token_job
383
+ ```
384
+
385
+ The generated rake task will be found at `lib/tasks/shopify/rotate_shopify_token.rake` and is provided strictly for example purposes. It might not work with your application out of the box without some configuration.
386
+
387
+ ⚠️ Note: if you are updating `shopify_app` from a version prior to 8.4.2 (and do not wish to run the default/install generator again), you will need to add [the following line](https://github.com/Shopify/shopify_app/blob/4f7e6cca2a472d8f7af44b938bd0fcafe4d8e88a/lib/generators/shopify_app/install/templates/shopify_provider.rb#L18) to `config/intializers/omniauth.rb`:
388
+
389
+ ```ruby
390
+ strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
391
+ ```
392
+
367
393
  ShopifyApp::SessionRepository
368
394
  -----------------------------
369
395
 
@@ -0,0 +1,21 @@
1
+ ---
2
+ nl:
3
+ logged_out: U bent afgemeld
4
+ could_not_log_in: Kon niet aanmelden bij Shopify-winkel
5
+ invalid_shop_url: Ongeldig winkeldomein
6
+ enable_cookies_heading: Schakel cookies in van %{app}
7
+ enable_cookies_body: U moet cookies in deze browser handmatig inschakelen om %{app}
8
+ binnen Shopify te gebruiken.
9
+ enable_cookies_footer: Met cookies kan de app u verifiëren door uw voorkeuren en
10
+ persoonlijke informatie tijdelijk op te slaan. Ze vervallen na 30 dagen.
11
+ enable_cookies_action: Schakel cookies in
12
+ top_level_interaction_heading: Uw browser moet %{app} verifiëren
13
+ top_level_interaction_body: Uw browser heeft apps van derden nodig zoals %{app}
14
+ om u toegang te vragen tot cookies voordat Shopify het voor u kan openen.
15
+ top_level_interaction_action: Doorgaan
16
+ request_storage_access_heading: "%{app} heeft toegang tot cookies nodig"
17
+ request_storage_access_body: Hiermee kan de app u verifiëren door uw persoonlijke
18
+ gegevens tijdelijk op te slaan. Klik op Doorgaan en sta cookies toe om de app
19
+ te gebruiken.
20
+ request_storage_access_footer: Cookies verlopen na 30 dagen.
21
+ request_storage_access_action: Doorgaan
@@ -0,0 +1,16 @@
1
+ ---
2
+ zh-CN:
3
+ logged_out: 已成功注销
4
+ could_not_log_in: 无法登录到 Shopify 店铺
5
+ invalid_shop_url: 商店域无效
6
+ enable_cookies_heading: 从 %{app} 启用 Cookie
7
+ enable_cookies_body: 您必须在此浏览器中手动启用 Cookie 才能在 Shopify 中使用 %{app}。
8
+ enable_cookies_footer: Cookie 使此应用能够通过暂时存储您的首选项和个人信息来验证您的身份。这些信息将在 30 天后过期。
9
+ enable_cookies_action: 启用 Cookie
10
+ top_level_interaction_heading: 您的浏览器需要对 %{app} 进行验证
11
+ top_level_interaction_body: 您的浏览器要求类似 %{app} 的第三方应用向您请求访问 Cookie,之后 Shopify 才能为您打开它。
12
+ top_level_interaction_action: 继续
13
+ request_storage_access_heading: "%{app} 需要访问 Cookie"
14
+ request_storage_access_body: 这使此应用能够通过暂时存储您的个人信息来验证您的身份。单击继续并启用 Cookie 以使用此应用。
15
+ request_storage_access_footer: Cookie 将在 30 天后过期。
16
+ request_storage_access_action: 继续
@@ -0,0 +1,17 @@
1
+ ---
2
+ zh-TW:
3
+ logged_out: 登出成功
4
+ could_not_log_in: 無法登入 Shopify 商店
5
+ invalid_shop_url: 商店網域無效
6
+ enable_cookies_heading: 啟用 %{app} 的 Cookie
7
+ enable_cookies_body: 您必須在此瀏覽器中手動啟用 Cookie,才能夠在 Shopify 使用 %{app}。
8
+ enable_cookies_footer: Cookie 可讓應用程式暫時儲存您的偏好設定和個人資訊,藉此驗證您的身分,這些資料會在 30 天後失效。
9
+ enable_cookies_action: 啟用 Cookie
10
+ top_level_interaction_heading: 您的瀏覽器需要驗證 %{app}
11
+ top_level_interaction_body: 您的瀏覽器會要求第三方應用程式 (例如:%{app}) 向您請求 Cookie 的存取權限,然後才讓 Shopify
12
+ 為您開啟此應用程式。
13
+ top_level_interaction_action: 繼續
14
+ request_storage_access_heading: "%{app} 需要 Cookie 存取權限"
15
+ request_storage_access_body: Cookie 可讓應用程式暫時儲存您的個人資訊,藉此驗證您的身分。按一下繼續並允許 Cookie 使用此應用程式。
16
+ request_storage_access_footer: Cookie 將於 30 天後失效。
17
+ request_storage_access_action: 繼續
@@ -12,7 +12,9 @@ module ShopifyApp
12
12
  def init_after_authenticate_config
13
13
  initializer = load_initializer
14
14
 
15
- after_authenticate_job_config = " config.after_authenticate_job = { job: Shopify::AfterAuthenticateJob, inline: false }\n"
15
+ after_authenticate_job_config =
16
+ " config.after_authenticate_job = "\
17
+ "{ job: Shopify::AfterAuthenticateJob, inline: false }\n"
16
18
 
17
19
  inject_into_file(
18
20
  'config/initializers/shopify_app.rb',
@@ -9,6 +9,7 @@ module ShopifyApp
9
9
  class_option :application_name, type: :array, default: ['My', 'Shopify', 'App']
10
10
  class_option :api_key, type: :string, default: '<api_key>'
11
11
  class_option :secret, type: :string, default: '<secret>'
12
+ class_option :old_secret, type: :string, default: '<old_secret>'
12
13
  class_option :scope, type: :array, default: ['read_products']
13
14
  class_option :embedded, type: :string, default: 'true'
14
15
 
@@ -16,6 +17,7 @@ module ShopifyApp
16
17
  @application_name = format_array_argument(options['application_name'])
17
18
  @api_key = options['api_key']
18
19
  @secret = options['secret']
20
+ @old_secret = options['old_secret']
19
21
  @scope = format_array_argument(options['scope'])
20
22
 
21
23
  template 'shopify_app.rb', 'config/initializers/shopify_app.rb'
@@ -2,6 +2,7 @@ ShopifyApp.configure do |config|
2
2
  config.application_name = "<%= @application_name %>"
3
3
  config.api_key = "<%= @api_key %>"
4
4
  config.secret = "<%= @secret %>"
5
+ config.old_secret = "<%= @old_secret %>"
5
6
  config.scope = "<%= @scope %>" # Consult this page for more scope options:
6
7
  # https://help.shopify.com/en/api/getting-started/authentication/oauth/scopes
7
8
  config.embedded_app = <%= embedded_app? %>
@@ -1,16 +1,19 @@
1
- provider :shopify,
2
- ShopifyApp.configuration.api_key,
3
- ShopifyApp.configuration.secret,
4
- scope: ShopifyApp.configuration.scope,
5
- setup: lambda { |env|
6
- strategy = env['omniauth.strategy']
1
+ # frozen_string_literal: true
7
2
 
8
- shopify_auth_params = strategy.session['shopify.omniauth_params']&.with_indifferent_access
9
- shop = if shopify_auth_params.present?
10
- "https://#{shopify_auth_params[:shop]}"
11
- else
12
- ''
13
- end
3
+ provider :shopify,
4
+ ShopifyApp.configuration.api_key,
5
+ ShopifyApp.configuration.secret,
6
+ scope: ShopifyApp.configuration.scope,
7
+ setup: lambda { |env|
8
+ strategy = env['omniauth.strategy']
14
9
 
15
- strategy.options[:client_options][:site] = shop
16
- }
10
+ shopify_auth_params = strategy.session['shopify.omniauth_params']&.with_indifferent_access
11
+ shop = if shopify_auth_params.present?
12
+ "https://#{shopify_auth_params[:shop]}"
13
+ else
14
+ ''
15
+ end
16
+
17
+ strategy.options[:client_options][:site] = shop
18
+ strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
19
+ }
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+
5
+ module ShopifyApp
6
+ module Generators
7
+ class RotateShopifyTokenJobGenerator < Rails::Generators::Base
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ def add_rotate_shopify_token_job
11
+ copy_file('rotate_shopify_token_job.rb', "app/jobs/shopify/rotate_shopify_token_job.rb")
12
+ copy_file('rotate_shopify_token.rake', "lib/tasks/shopify/rotate_shopify_token.rake")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ namespace :shopify do
3
+ desc "Rotate shopify tokens for all active shops"
4
+ task :rotate_shopify_tokens, [:refresh_token] => :environment do |_t, args|
5
+ all_active_shops.find_each do |shop|
6
+ Shopify::RotateShopifyTokenJob.perform_later(
7
+ shop_domain: shop.shopify_domain,
8
+ refresh_token: args[:refresh_token]
9
+ )
10
+ end
11
+ end
12
+
13
+ # Its implementation will depend on the app configuration. Change accordingly.
14
+ def all_active_shops
15
+ Shop.all
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shopify
4
+ class RotateShopifyTokenJob < ActiveJob::Base
5
+ def perform(params)
6
+ @shop = Shop.find_by(shopify_domain: params[:shop_domain])
7
+ return unless @shop
8
+
9
+ config = ShopifyApp.configuration
10
+ uri = URI("https://#{@shop.shopify_domain}/admin/oauth/access_token")
11
+ post_data = {
12
+ client_id: config.api_key,
13
+ client_secret: config.secret,
14
+ refresh_token: params[:refresh_token],
15
+ access_token: @shop.shopify_token,
16
+ }
17
+
18
+ @response = Net::HTTP.post_form(uri, post_data)
19
+ return log_error(response_expcetion_error_message) unless @response.is_a?(Net::HTTPSuccess)
20
+
21
+ access_token = JSON.parse(@response.body)['access_token']
22
+ return log_error(no_access_token_error_message) unless access_token
23
+
24
+ @shop.update(shopify_token: access_token)
25
+ end
26
+
27
+ private
28
+
29
+ def log_error(message)
30
+ Rails.logger.error(message)
31
+ end
32
+
33
+ def no_access_token_error_message
34
+ "RotateShopifyTokenJob response returned no access token for shop: #{@shop.shopify_domain}"
35
+ end
36
+
37
+ def response_exception_error_message
38
+ "RotateShopifyTokenJob failed for shop: #{@shop.shopify_domain}." \
39
+ "Response returned status: #{@response.code}. Error message: #{@response.message}. "
40
+ end
41
+ end
42
+ end
@@ -7,6 +7,7 @@ module ShopifyApp
7
7
  attr_accessor :application_name
8
8
  attr_accessor :api_key
9
9
  attr_accessor :secret
10
+ attr_accessor :old_secret
10
11
  attr_accessor :scope
11
12
  attr_accessor :embedded_app
12
13
  alias_method :embedded_app?, :embedded_app
@@ -15,12 +15,16 @@ module ShopifyApp
15
15
  end
16
16
 
17
17
  def hmac_valid?(data)
18
- secret = ShopifyApp.configuration.secret
19
- digest = OpenSSL::Digest.new('sha256')
20
- ActiveSupport::SecurityUtils.secure_compare(
21
- shopify_hmac,
22
- Base64.encode64(OpenSSL::HMAC.digest(digest, secret, data)).strip
23
- )
18
+ secrets = [ShopifyApp.configuration.secret, ShopifyApp.configuration.old_secret].reject(&:blank?)
19
+
20
+ secrets.any? do |secret|
21
+ digest = OpenSSL::Digest.new('sha256')
22
+
23
+ ActiveSupport::SecurityUtils.secure_compare(
24
+ shopify_hmac,
25
+ Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, data))
26
+ )
27
+ end
24
28
  end
25
29
 
26
30
  def shop_domain
@@ -1,3 +1,3 @@
1
1
  module ShopifyApp
2
- VERSION = '8.4.2'.freeze
2
+ VERSION = '8.5.0'.freeze
3
3
  end
data/shopify_app.gemspec CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.add_runtime_dependency('browser_sniffer', '~> 1.1.0')
14
14
  s.add_runtime_dependency('rails', '>= 5.0.0')
15
15
  s.add_runtime_dependency('shopify_api', '>= 4.3.5')
16
- s.add_runtime_dependency('omniauth-shopify-oauth2', '~> 2.0.0')
16
+ s.add_runtime_dependency('omniauth-shopify-oauth2', '~> 2.1.0')
17
17
 
18
18
  s.add_development_dependency('rake')
19
19
  s.add_development_dependency('byebug')
data/translation.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  source_language: en
2
- target_languages: [de, es, fr, it, ja, pt-BR]
2
+ target_languages: [de, es, fr, it, ja, nl, pt-BR, zh-CN, zh-TW]
3
3
  components:
4
4
  - name: 'merchant'
5
5
  paths:
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: 8.4.2
4
+ version: 8.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-11 00:00:00.000000000 Z
11
+ date: 2019-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: browser_sniffer
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 2.0.0
61
+ version: 2.1.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 2.0.0
68
+ version: 2.1.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -186,7 +186,10 @@ files:
186
186
  - config/locales/fr.yml
187
187
  - config/locales/it.yml
188
188
  - config/locales/ja.yml
189
+ - config/locales/nl.yml
189
190
  - config/locales/pt-BR.yml
191
+ - config/locales/zh-CN.yml
192
+ - config/locales/zh-TW.yml
190
193
  - config/routes.rb
191
194
  - docs/Quickstart.md
192
195
  - docs/Releasing.md
@@ -212,6 +215,9 @@ files:
212
215
  - lib/generators/shopify_app/install/templates/omniauth.rb
213
216
  - lib/generators/shopify_app/install/templates/shopify_app.rb
214
217
  - lib/generators/shopify_app/install/templates/shopify_provider.rb
218
+ - lib/generators/shopify_app/rotate_shopify_token_job/rotate_shopify_token_job_generator.rb
219
+ - lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token.rake
220
+ - lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb
215
221
  - lib/generators/shopify_app/routes/routes_generator.rb
216
222
  - lib/generators/shopify_app/routes/templates/routes.rb
217
223
  - lib/generators/shopify_app/shop_model/shop_model_generator.rb