shopify_app 8.4.2 → 8.5.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.
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