shopify_app 11.5.1 → 12.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/README.md +122 -115
  4. data/app/assets/javascripts/shopify_app/itp_helper.js +6 -6
  5. data/app/assets/javascripts/shopify_app/storage_access.js +35 -6
  6. data/app/controllers/concerns/shopify_app/authenticated.rb +1 -1
  7. data/app/controllers/shopify_app/callback_controller.rb +8 -2
  8. data/app/controllers/shopify_app/extension_verification_controller.rb +20 -0
  9. data/app/controllers/shopify_app/sessions_controller.rb +8 -6
  10. data/app/views/shopify_app/sessions/enable_cookies.html.erb +1 -1
  11. data/app/views/shopify_app/sessions/request_storage_access.html.erb +1 -1
  12. data/config/locales/pt-BR.yml +1 -1
  13. data/docs/Quickstart.md +44 -16
  14. data/docs/install-on-dev-shop.png +0 -0
  15. data/docs/test-your-app.png +0 -0
  16. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +2 -6
  17. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +0 -6
  18. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +2 -2
  19. data/lib/generators/shopify_app/install/templates/flash_messages.js +11 -2
  20. data/lib/generators/shopify_app/install/templates/shopify_app.js +9 -3
  21. data/lib/generators/shopify_app/install/templates/shopify_provider.rb +1 -0
  22. data/lib/generators/shopify_app/user_model/templates/db/migrate/create_users.erb +16 -0
  23. data/lib/generators/shopify_app/user_model/templates/user.rb +7 -0
  24. data/lib/generators/shopify_app/user_model/templates/users.yml +4 -0
  25. data/lib/generators/shopify_app/user_model/user_model_generator.rb +38 -0
  26. data/lib/shopify_app.rb +5 -3
  27. data/lib/shopify_app/configuration.rb +10 -0
  28. data/lib/shopify_app/controller_concerns/login_protection.rb +33 -6
  29. data/lib/shopify_app/engine.rb +4 -0
  30. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +60 -0
  31. data/lib/shopify_app/session/in_memory_session_store.rb +1 -1
  32. data/lib/shopify_app/session/session_repository.rb +2 -2
  33. data/lib/shopify_app/session/session_storage.rb +10 -22
  34. data/lib/shopify_app/session/storage_strategies/shop_storage_strategy.rb +23 -0
  35. data/lib/shopify_app/session/storage_strategies/user_storage_strategy.rb +24 -0
  36. data/lib/shopify_app/version.rb +1 -1
  37. data/package.json +1 -0
  38. data/service.yml +1 -1
  39. data/shopify_app.gemspec +5 -2
  40. metadata +56 -6
  41. data/lib/generators/shopify_app/home_controller/templates/shopify_app_ready_script.html.erb +0 -7
  42. data/lib/shopify_app/controllers/extension_verification_controller.rb +0 -18
@@ -8,7 +8,7 @@ module ShopifyApp
8
8
  include ShopifyApp::Localization
9
9
  include ShopifyApp::LoginProtection
10
10
  include ShopifyApp::EmbeddedApp
11
- before_action :login_again_if_different_shop
11
+ before_action :login_again_if_different_user_or_shop
12
12
  around_action :shopify_session
13
13
  end
14
14
  end
@@ -55,10 +55,16 @@ module ShopifyApp
55
55
  token: token,
56
56
  api_version: ShopifyApp.configuration.api_version
57
57
  )
58
-
59
- session[:shopify] = ShopifyApp::SessionRepository.store(session_store)
58
+ session[:shopify] = ShopifyApp::SessionRepository.store(session_store, user: associated_user)
60
59
  session[:shopify_domain] = shop_name
61
60
  session[:shopify_user] = associated_user
61
+
62
+ if ShopifyApp.configuration.per_user_tokens?
63
+ # Adds the user_session to the session to determine if the logged in user has changed
64
+ user_session = auth_hash&.extra&.session
65
+ raise IndexError, "Missing user session signature" if user_session.nil?
66
+ session[:user_session] = user_session
67
+ end
62
68
  end
63
69
 
64
70
  def install_webhooks
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ class ExtensionVerificationController < ActionController::Base
5
+ protect_from_forgery with: :null_session
6
+ before_action :verify_request
7
+
8
+ private
9
+
10
+ def verify_request
11
+ hmac_header = request.headers['HTTP_X_SHOPIFY_HMAC_SHA256']
12
+ request_body = request.body.read
13
+ secret = ShopifyApp.configuration.secret
14
+ digest = OpenSSL::Digest.new('sha256')
15
+
16
+ expected_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, request_body))
17
+ head(:unauthorized) unless ActiveSupport::SecurityUtils.secure_compare(expected_hmac, hmac_header)
18
+ end
19
+ end
20
+ end
@@ -20,11 +20,12 @@ module ShopifyApp
20
20
 
21
21
  render(:enable_cookies, layout: false, locals: {
22
22
  does_not_have_storage_access_url: top_level_interaction_path(
23
- shop: sanitized_shop_name
23
+ shop: sanitized_shop_name,
24
+ return_to: params[:return_to]
24
25
  ),
25
26
  has_storage_access_url: login_url_with_optional_shop(top_level: true),
26
- app_home_url: granted_storage_access_path(shop: sanitized_shop_name),
27
- current_shopify_domain: current_shopify_domain,
27
+ app_target_url: params[:return_to] || granted_storage_access_path(shop: sanitized_shop_name),
28
+ current_shopify_domain: current_shopify_domain
28
29
  })
29
30
  end
30
31
 
@@ -133,11 +134,12 @@ module ShopifyApp
133
134
  layout: false,
134
135
  locals: {
135
136
  does_not_have_storage_access_url: top_level_interaction_path(
136
- shop: sanitized_shop_name
137
+ shop: sanitized_shop_name,
138
+ return_to: session[:return_to]
137
139
  ),
138
140
  has_storage_access_url: login_url_with_optional_shop(top_level: true),
139
- app_home_url: granted_storage_access_path(shop: sanitized_shop_name),
140
- current_shopify_domain: current_shopify_domain,
141
+ app_target_url: session[:return_to] || granted_storage_access_path(shop: sanitized_shop_name),
142
+ current_shopify_domain: current_shopify_domain
141
143
  }
142
144
  )
143
145
  end
@@ -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
- appHomeUrl: "#{app_home_url}"
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
- appHomeUrl: "#{app_home_url}"
27
+ appTargetUrl: "#{app_target_url}"
28
28
  },
29
29
  },
30
30
  )
@@ -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ê deve habilitar manualmente os cookies neste navegador
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.
@@ -1,11 +1,13 @@
1
1
  Quickstart
2
2
  ==========
3
3
 
4
- Build and deploy a new Shopify App to Heroku in minutes
4
+ Get started building and deploying a new Shopify App to Heroku in just a few minutes. This guide assumes you have Ruby/Rails installed on your computer already; if you haven't done that already start with [this guide.](https://guides.rubyonrails.org/v5.0/getting_started.html#installing-rails)
5
5
 
6
6
  1. New Rails App (with postgres)
7
7
  --------------------------------
8
8
 
9
+ To create a new Rails app and use this generator, open your terminal and run the following commands:
10
+
9
11
  ```sh
10
12
  $ rails new test-app --database=postgresql
11
13
  $ cd test-app
@@ -17,43 +19,51 @@ $ git commit -m 'new rails app'
17
19
  2. Create a new Heroku app
18
20
  --------------------------
19
21
 
20
- The next step is to create a new heroku app. Pull up your heroku dashboard and make a new app!
22
+ The next step is to create a new Heroku app to host your application. If you haven't got a Heroku account yet, create a free account [here](https://www.heroku.com/).
23
+
24
+ Head to the Heroku dashboard and create a new app, or run the following commands with the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli#download-and-install) installed, substituting `name` for the name of your own app:
21
25
 
22
- cli:
26
+ CLI:
23
27
  ```sh
24
28
  $ heroku create name
25
29
  $ heroku git:remote -a name
26
30
  ```
27
31
 
28
- now we need to let git know where the remote server is so we'll be able to deploy later
32
+ Once you have created an app on Heroku, we need to let Git know where the Heroku server is so we can deploy to it later. Copy the app's name from your Heroku dashboard and substitute `appname.git` with the name you chose earlier:
29
33
 
30
34
  web:
31
35
  ```sh
32
36
  # https://dashboard.heroku.com/new
33
- $ git remote add heroku git@heroku.com:appinfive.git
37
+ $ git remote add heroku git@heroku.com:appname.git
34
38
  ```
35
39
 
36
- 3. Create a new App in the partners area
40
+ 3. Create a new App in the Shopify Partner dashboard
37
41
  -----------------------------------------
42
+ * Create a Shopify app in the [Partners dashboard](https://partner.shopify.com). For this tutorial, you can choose either a public or custom app, but you can [learn about App Types here.](https://help.shopify.com/en/manual/apps/app-types)
38
43
  [https://app.shopify.com/services/partners/api_clients](https://app.shopify.com/services/partners/api_clients)
39
- * set the callback url to `https://<name>.herokuapp.com/`
40
- * choose an embedded app
41
- * set the redirect_uri to `https://<name>.herokuapp.com/auth/shopify/callback`
42
-
44
+ * Set the callback url to `https://<appname>.herokuapp.com/`
45
+ * Choose an embedded app
46
+ * Set the app's `redirect_uri` to `https://<appname>.herokuapp.com/auth/shopify/callback`
43
47
 
44
- 4. Add ShopifyApp to gemfile
48
+ 4. Add ShopifyApp to Gemfile
45
49
  ----------------------------
50
+
51
+ Run these commands to add the `shopify_app` Gem to your app:
52
+
46
53
  ```sh
47
54
  $ echo "gem 'shopify_app'" >> Gemfile
48
55
  $ bundle install
49
56
  ```
50
57
 
51
- Note - its recommended to use the latest released version. Check the git tags to see the latest release and then add it to your Gemfile e.g `gem 'shopify_app', '~> 7.0.0'`
58
+ **Note:** we recommend using the latest version of Shopify Gem. Check the [Git tags](https://github.com/Shopify/shopify_app/tags) to see the latest release version and then add it to your Gemfile e.g `gem 'shopify_app', '~> 7.0.0'`
52
59
 
53
60
  5. Run the ShopifyApp generator
54
61
  -------------------------------
62
+
63
+ Generate the code for your app by running these commands:
64
+
55
65
  ```sh
56
- # use the keys from your app in the partners area
66
+ # Use the keys from your app you created in the partners area
57
67
  $ rails generate shopify_app --api_key <shopify_api_key> --secret <shopify_api_secret>
58
68
  $ git add .
59
69
  $ git commit -m 'generated shopify app'
@@ -61,10 +71,12 @@ $ git commit -m 'generated shopify app'
61
71
 
62
72
  If you forget to set your keys or redirect uri above, you will find them in the shopify_app initializer at: `/config/initializers/shopify_app.rb`.
63
73
 
64
- We recommend adding a gem or utilizing ENV variables to handle your keys before releasing your app.
74
+ We recommend adding a gem or utilizing environment variables (`.env`) to handle your keys before releasing your app. [Learn more about using environment variables.](https://www.honeybadger.io/blog/ruby-guide-environment-variables/)
65
75
 
66
- 6. Deploy
76
+ 6. Deploy your app
67
77
  ---------
78
+
79
+ Once you've generated your app, push it into your Heroku environment to see it up and running:
68
80
  ```sh
69
81
  $ git push heroku
70
82
  $ heroku run rake db:migrate
@@ -72,4 +84,20 @@ $ heroku run rake db:migrate
72
84
 
73
85
  7. Install the App!
74
86
  -------------------
75
- `https://<name>.herokuapp.com/`
87
+
88
+ Ensure you have created a [development store](https://help.shopify.com/en/api/getting-started/making-your-first-request#create-a-development-store) using the Shopify Partner Dashboard. If you don't already have one, [create one by following these instructions](https://help.shopify.com/en/api/getting-started/making-your-first-request#create-a-development-store).
89
+
90
+ ##### Note: The following step will cause your store to become `transfer-disabled.` Read more about store transfer and why it's important [here](https://help.shopify.com/en/api/guides/store-transfers#transfer-disabled-stores). This is an irreversible change, so be sure you don't plan to transfer this store to a merchant.
91
+
92
+ Install the app onto your new development store using the Partner Dashboard. Log in to your account, visit the apps page, click the app you created earlier, and looking for the `Test your app` instructions where you can select a store to install your app on.
93
+
94
+ ![Installing an app on the partners dashboard dropdown](/docs/install-on-dev-shop.png)
95
+
96
+ ### OR
97
+
98
+ ![Installing an app on the partners dashboard card](/docs/test-your-app.png)
99
+
100
+ 8. Great work!
101
+ -------------------
102
+
103
+ You're done creating your first app on Shopify. Keep going and learn more by [diving into our full documentation](https://help.shopify.com/en/api/getting-started), or join our [community of developers.](https://community.shopify.com/c/Shopify-Apps/bd-p/shopify-apps)
Binary file
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class MarketingActivitiesController < ExtensionVerificationController
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
@@ -11,12 +11,6 @@ module ShopifyApp
11
11
 
12
12
  def create_home_index_view
13
13
  copy_file 'index.html.erb', 'app/views/home/index.html.erb'
14
- if embedded_app?
15
- prepend_to_file(
16
- 'app/views/home/index.html.erb',
17
- File.read(File.expand_path(find_in_source_paths('shopify_app_ready_script.html.erb')))
18
- )
19
- end
20
14
  end
21
15
 
22
16
  def add_home_index_route
@@ -24,11 +24,11 @@
24
24
 
25
25
  <%= render 'layouts/flash_messages' %>
26
26
 
27
- <script src="https://cdn.shopify.com/s/assets/external/app.js?<%= Time.now.strftime('%Y%m%d%H') %>"></script>
27
+ <script src="https://unpkg.com/@shopify/app-bridge"></script>
28
28
 
29
29
  <%= content_tag(:div, nil, id: 'shopify-app-init', data: {
30
30
  api_key: ShopifyApp.configuration.api_key,
31
- shop_origin: ("https://#{ @shop_session.url }" if @shop_session),
31
+ shop_origin: (@shop_session.domain if @shop_session),
32
32
  debug: Rails.env.development?
33
33
  } ) %>
34
34
 
@@ -4,12 +4,21 @@ if (!document.documentElement.hasAttribute("data-turbolinks-preview")) {
4
4
  document.addEventListener(eventName, function flash() {
5
5
  var flashData = JSON.parse(document.getElementById('shopify-app-flash').dataset.flash);
6
6
 
7
+ var Toast = window['app-bridge'].actions.Toast;
8
+
7
9
  if (flashData.notice) {
8
- ShopifyApp.flashNotice(flashData.notice);
10
+ Toast.create(app, {
11
+ message: flashData.notice,
12
+ duration: 5000,
13
+ }).dispatch(Toast.Action.SHOW);
9
14
  }
10
15
 
11
16
  if (flashData.error) {
12
- ShopifyApp.flashError(flashData.error);
17
+ Toast.create(app, {
18
+ message: flashData.error,
19
+ duration: 5000,
20
+ isError: true,
21
+ }).dispatch(Toast.Action.SHOW);
13
22
  }
14
23
 
15
24
  document.removeEventListener(eventName, flash)
@@ -1,9 +1,15 @@
1
1
  document.addEventListener('DOMContentLoaded', () => {
2
2
  var data = document.getElementById('shopify-app-init').dataset;
3
- ShopifyApp.init({
3
+ var AppBridge = window['app-bridge'];
4
+ var createApp = AppBridge.default;
5
+ window.app = createApp({
4
6
  apiKey: data.apiKey,
5
7
  shopOrigin: data.shopOrigin,
6
- debug: data.debug === 'true',
7
- forceRedirect: true
8
+ });
9
+
10
+ var actions = AppBridge.actions;
11
+ var TitleBar = actions.TitleBar;
12
+ TitleBar.create(app, {
13
+ title: data.page,
8
14
  });
9
15
  });
@@ -4,6 +4,7 @@ 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,
7
8
  setup: lambda { |env|
8
9
  strategy = env['omniauth.strategy']
9
10
 
@@ -0,0 +1,16 @@
1
+ class CreateUsers < ActiveRecord::Migration[<%= rails_migration_version %>]
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.bigint :shopify_user_id, null: false
5
+ t.string :shopify_domain, null: false
6
+ t.string :shopify_token, null: false
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :users, :shopify_user_id, unique: true
11
+ end
12
+
13
+ def self.down
14
+ drop_table :users
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ class User < ActiveRecord::Base
2
+ include ShopifyApp::SessionStorage
3
+
4
+ def api_version
5
+ ShopifyApp.configuration.api_version
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ regular_user:
2
+ shopify_domain: 'regular-shop.myshopify.com'
3
+ shopify_token: 'token'
4
+ shopify_user_id: 1
@@ -0,0 +1,38 @@
1
+ require 'rails/generators/base'
2
+ require 'rails/generators/active_record'
3
+
4
+ module ShopifyApp
5
+ module Generators
6
+ class UserModelGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ def create_user_model
11
+ copy_file 'user.rb', 'app/models/user.rb'
12
+ end
13
+
14
+ def create_user_migration
15
+ migration_template 'db/migrate/create_users.erb', 'db/migrate/create_users.rb'
16
+ end
17
+
18
+ def update_shopify_app_initializer
19
+ gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemorySessionStore', 'User'
20
+ end
21
+
22
+ def create_user_fixtures
23
+ copy_file 'users.yml', 'test/fixtures/users.yml'
24
+ end
25
+
26
+ private
27
+
28
+ def rails_migration_version
29
+ Rails.version.match(/\d\.\d/)[0]
30
+ end
31
+
32
+ # for generating a timestamp when using `create_migration`
33
+ def self.next_migration_number(dir)
34
+ ActiveRecord::Generators::Base.next_migration_number(dir)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -24,9 +24,6 @@ module ShopifyApp
24
24
  # utils
25
25
  require 'shopify_app/utils'
26
26
 
27
- # controllers
28
- require 'shopify_app/controllers/extension_verification_controller'
29
-
30
27
  # controller concerns
31
28
  require 'shopify_app/controller_concerns/localization'
32
29
  require 'shopify_app/controller_concerns/itp'
@@ -43,7 +40,12 @@ module ShopifyApp
43
40
  require 'shopify_app/managers/webhooks_manager'
44
41
  require 'shopify_app/managers/scripttags_manager'
45
42
 
43
+ # middleware
44
+ require 'shopify_app/middleware/same_site_cookie_middleware'
45
+
46
46
  # session
47
+ require 'shopify_app/session/storage_strategies/shop_storage_strategy'
48
+ require 'shopify_app/session/storage_strategies/user_storage_strategy'
47
49
  require 'shopify_app/session/session_storage'
48
50
  require 'shopify_app/session/session_repository'
49
51
  require 'shopify_app/session/in_memory_session_store'
@@ -15,6 +15,8 @@ module ShopifyApp
15
15
  attr_accessor :scripttags
16
16
  attr_accessor :after_authenticate_job
17
17
  attr_reader :session_repository
18
+ attr_accessor :per_user_tokens
19
+ alias_method :per_user_tokens?, :per_user_tokens
18
20
  attr_accessor :api_version
19
21
 
20
22
  # customise urls
@@ -34,11 +36,15 @@ module ShopifyApp
34
36
  # allow namespacing webhook jobs
35
37
  attr_accessor :webhook_jobs_namespace
36
38
 
39
+ # allow enabling of same site none on cookies
40
+ attr_accessor :enable_same_site_none
41
+
37
42
  def initialize
38
43
  @root_url = '/'
39
44
  @myshopify_domain = 'myshopify.com'
40
45
  @scripttags_manager_queue_name = Rails.application.config.active_job.queue_name
41
46
  @webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
47
+ @per_user_tokens = false
42
48
  @disable_webpacker = ENV['SHOPIFY_APP_DISABLE_WEBPACKER'].present?
43
49
  end
44
50
 
@@ -58,6 +64,10 @@ module ShopifyApp
58
64
  def has_scripttags?
59
65
  scripttags.present?
60
66
  end
67
+
68
+ def enable_same_site_none
69
+ !Rails.env.test? && (@enable_same_site_none.nil? ? embedded_app? : @enable_same_site_none)
70
+ end
61
71
  end
62
72
 
63
73
  def self.configuration