shopify_app 13.3.0 → 14.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +6 -0
  3. data/.travis.yml +0 -1
  4. data/CHANGELOG.md +22 -0
  5. data/README.md +12 -3
  6. data/app/assets/javascripts/shopify_app/app_bridge_redirect.js +18 -0
  7. data/app/assets/javascripts/shopify_app/redirect.js +4 -9
  8. data/app/assets/javascripts/shopify_app/storage_access.js +3 -11
  9. data/app/controllers/concerns/shopify_app/authenticated.rb +1 -0
  10. data/app/controllers/shopify_app/callback_controller.rb +2 -2
  11. data/app/views/shopify_app/sessions/enable_cookies.html.erb +6 -6
  12. data/app/views/shopify_app/sessions/request_storage_access.html.erb +5 -0
  13. data/app/views/shopify_app/shared/redirect.html.erb +6 -1
  14. data/docs/Quickstart.md +2 -3
  15. data/docs/Releasing.md +12 -12
  16. data/lib/generators/shopify_app/authenticated_controller/authenticated_controller_generator.rb +1 -1
  17. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +20 -2
  18. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +67 -17
  19. data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +10 -0
  20. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
  21. data/lib/generators/shopify_app/install/templates/flash_messages.js +0 -2
  22. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +1 -0
  23. data/lib/generators/shopify_app/products_controller/products_controller_generator.rb +19 -0
  24. data/lib/generators/shopify_app/products_controller/templates/products_controller.rb +8 -0
  25. data/lib/generators/shopify_app/shopify_app_generator.rb +1 -1
  26. data/lib/shopify_app.rb +1 -0
  27. data/lib/shopify_app/controller_concerns/csrf_protection.rb +15 -0
  28. data/lib/shopify_app/controller_concerns/login_protection.rb +8 -0
  29. data/lib/shopify_app/version.rb +1 -1
  30. data/package-lock.json +7 -75
  31. data/package.json +2 -2
  32. data/shopify_app.gemspec +2 -2
  33. data/yarn.lock +248 -178
  34. metadata +11 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6666c7899ac8e3332bf950ce28d445e2fc40886c811631a4bfb3f1e446c286e0
4
- data.tar.gz: f85b1b7ed7a77e2430ed955ed0f65bd30b5021d85614da8943332077a999d38e
3
+ metadata.gz: db65febc756d6f85ea5ee8f2d109e5e8fa8087ed4b1eaa3df2e1d0abfa33e95c
4
+ data.tar.gz: 74c591a7dc4f9f6d0fc88f5be084decd7746a6052a72698e34938caeb8b4ef49
5
5
  SHA512:
6
- metadata.gz: 6582a20d5b5340e7259aef06660e0a2673f31cc0b9413d6d8fda0a7c7fa34982f7dc08cd69f78cad2a0d4e322aedddcc6e472ba368ac4066f1215dc5daaacc4d
7
- data.tar.gz: c9bf293b825e474f3ef73353428309baa0c3536c7781597b58ca083fc03b587e210e05dbbdc398edf235b225a537dd3dd02bb612176625d42743a2ae1978aeec
6
+ metadata.gz: 76e46bf49ceb4d417df9e414c2bb957bff0726f8a1e54bcb87df36a19ec1efe7b32c71dda81b8f1d0425acfccedd375149c5e975ac7f7281d3bb62136d578b49
7
+ data.tar.gz: e2459eb498a8b220d888fc1412231b2ce736cd779a4cfac6aa9fa1ce61cbe111a17d2a89427d6e0e38e67a9b3c201a5c0cfcba63c624dcdc27dbd5d8417dd47c
@@ -0,0 +1,6 @@
1
+ Before submitting the PR, please consider if any of the following are needed:
2
+
3
+ - [ ] Update `CHANGELOG.md` if the changes would impact users
4
+ - [ ] Update `README.md`, if appropriate.
5
+ - [ ] Update any relevant pages in `docs/`, if necessary
6
+ - [ ] For security fixes, the [Disclosure Policy](https://github.com/Shopify/shopify_app/blob/master/SECURITY.md#disclosure-policy) must be followed.
@@ -13,7 +13,6 @@ cache:
13
13
  yarn: true
14
14
 
15
15
  rvm:
16
- - 2.4
17
16
  - 2.5
18
17
  - 2.6
19
18
  - 2.7
@@ -1,3 +1,25 @@
1
+ 14.1.0
2
+ ------
3
+ * Replace redirect calls to use App Bridge redirect functionality
4
+
5
+ 14.0.0
6
+ ------
7
+ * Ruby 2.4 is no longer supported by this gem
8
+ * Bump gemspec ruby dependency to 2.5
9
+ * (Beta) Add `--with-session-token` flag to the Shopify App generator to create an app that is compatible with App Bridge Authentication
10
+
11
+ 13.5.0
12
+ ------
13
+ * Add `signal_access_token_required` helper method for apps to indicate access token has expired and that a new one is required
14
+
15
+ 13.4.1
16
+ ------
17
+ * Fix the version checks for the dependency on `shopify_api` to allow all of v9.X
18
+
19
+ 13.4.0
20
+ ------
21
+ * Skip CSRF protection if a valid signed JWT token is present as we trust Shopify to be the only source that can sign it securely. [#994](https://github.com/Shopify/shopify_app/pull/994)
22
+
1
23
  13.3.0
2
24
  ------
3
25
  * Added Payload Verification module [#992](https://github.com/Shopify/shopify_app/pull/992)
data/README.md CHANGED
@@ -58,8 +58,7 @@ $ rails new my_shopify_app
58
58
  $ cd my_shopify_app
59
59
 
60
60
  # Add the gem shopify_app to your Gemfile
61
- $ echo "gem 'shopify_app'" >> Gemfile
62
- $ bundle install
61
+ $ bundle add shopify_app
63
62
  ```
64
63
 
65
64
  Now we are ready to run any of the [generators](#generators) included with `shopify_app`. The following section explains the generators and what you can do with them.
@@ -75,7 +74,7 @@ Generators
75
74
 
76
75
  ### Default Generator
77
76
 
78
- The default generator will run the `install`, `shop`, and `home_controller` generators. This is the recommended way to start a new app from scratch:
77
+ The default generator will run the `install`, `shop`, `authenticated_controller`, and `home_controller` generators. This is the recommended way to start a new app from scratch:
79
78
 
80
79
  ```sh
81
80
  $ rails generate shopify_app
@@ -124,6 +123,16 @@ $ rails generate shopify_app:home_controller
124
123
 
125
124
  This generator creates an example home controller and view which fetches and displays products using the Shopify API.
126
125
 
126
+ Options include:
127
+ * __[beta]__ `with-session-token`: This flag generates an unauthenticated home_controller and a protected sample products_controller. It also creates a home view that leverages a session token to fetch products from your products_controller. Use this flag if you plan to build a single-page application or to secure your app using JWT session tokens (e.g. `--with-session-token` or `--with-session-token true`).
128
+
129
+ ### Products Controller Generator
130
+
131
+ ```sh
132
+ $ rails generate shopify_app:products_controller
133
+ ```
134
+
135
+ This generator creates an example products API controller to fetch products using the Shopify API.
127
136
 
128
137
  ### App Proxy Controller Generator
129
138
 
@@ -0,0 +1,18 @@
1
+ (function(window) {
2
+ function appBridgeRedirect(url) {
3
+ var AppBridge = window['app-bridge'];
4
+ var createApp = AppBridge.default;
5
+ var Redirect = AppBridge.actions.Redirect;
6
+ var app = createApp({
7
+ apiKey: window.apiKey,
8
+ shopOrigin: window.shopOrigin.replace(/^https:\/\//, ''),
9
+ });
10
+
11
+ var normalizedLink = document.createElement('a');
12
+ normalizedLink.href = url;
13
+
14
+ Redirect.create(app).dispatch(Redirect.Action.REMOTE, normalizedLink.href);
15
+ }
16
+
17
+ window.appBridgeRedirect = appBridgeRedirect;
18
+ })(window);
@@ -1,3 +1,5 @@
1
+ //= require ./app_bridge_redirect.js
2
+
1
3
  (function() {
2
4
  function redirect() {
3
5
  var redirectTargetElement = document.getElementById("redirection-target");
@@ -12,15 +14,8 @@
12
14
  // If the current window is the 'parent', change the URL by setting location.href
13
15
  window.top.location.href = targetInfo.url;
14
16
  } else {
15
- // If the current window is the 'child', change the parent's URL with postMessage
16
- normalizedLink = document.createElement('a');
17
- normalizedLink.href = targetInfo.url;
18
-
19
- data = JSON.stringify({
20
- message: 'Shopify.API.remoteRedirect',
21
- data: {location: normalizedLink.href}
22
- });
23
- window.parent.postMessage(data, targetInfo.myshopifyUrl);
17
+ // If the current window is the 'child', change the parent's URL with App Bridge Redirect
18
+ window.appBridgeRedirect(targetInfo.url);
24
19
  }
25
20
  }
26
21
 
@@ -1,3 +1,5 @@
1
+ //= require ./app_bridge_redirect.js
2
+
1
3
  (function() {
2
4
  var ACCESS_GRANTED_STATUS = 'storage_access_granted';
3
5
  var ACCESS_DENIED_STATUS = 'storage_access_denied';
@@ -11,17 +13,7 @@
11
13
  }
12
14
 
13
15
  StorageAccessHelper.prototype.redirectToAppTLD = function(storageAccessStatus) {
14
- var normalizedLink = document.createElement('a');
15
-
16
- normalizedLink.href = this.setNormalizedLink(storageAccessStatus);
17
-
18
- data = JSON.stringify({
19
- message: 'Shopify.API.remoteRedirect',
20
- data: {
21
- location: normalizedLink.href,
22
- }
23
- });
24
- window.parent.postMessage(data, this.redirectData.myshopifyUrl);
16
+ window.appBridgeRedirect(this.setNormalizedLink(storageAccessStatus));
25
17
  }
26
18
 
27
19
  StorageAccessHelper.prototype.redirectToAppsIndex = function() {
@@ -7,6 +7,7 @@ module ShopifyApp
7
7
  included do
8
8
  include ShopifyApp::Localization
9
9
  include ShopifyApp::LoginProtection
10
+ include ShopifyApp::CsrfProtection
10
11
  include ShopifyApp::EmbeddedApp
11
12
  before_action :login_again_if_different_user_or_shop
12
13
  around_action :activate_shopify_session
@@ -65,9 +65,9 @@ module ShopifyApp
65
65
  end
66
66
 
67
67
  def associated_user
68
- return unless auth_hash['extra'].present?
68
+ return unless auth_hash.dig('extra', 'associated_user').present?
69
69
 
70
- auth_hash['extra']['associated_user']
70
+ auth_hash['extra']['associated_user'].merge('scope' => auth_hash['extra']['associated_user_scope'])
71
71
  end
72
72
 
73
73
  def associated_user_id
@@ -5,6 +5,12 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <base target="_top">
7
7
  <title>Redirecting…</title>
8
+ <script src="https://unpkg.com/@shopify/app-bridge@^1"></script>
9
+ <script>
10
+ window.apiKey = "<%= ShopifyApp.configuration.api_key %>";
11
+ window.shopOrigin = "https://<%= @shop %>";
12
+ window.returnTo = "<%= params[:return_to] %>"
13
+ </script>
8
14
  <%= render 'shopify_app/partials/layout_styles' %>
9
15
  <%= render 'shopify_app/partials/typography_styles' %>
10
16
  <%= render 'shopify_app/partials/card_styles' %>
@@ -14,12 +20,6 @@
14
20
  display: none;
15
21
  }
16
22
  </style>
17
- <script>
18
- window.apiKey = "<%= ShopifyApp.configuration.api_key %>";
19
- window.shopOrigin = "https://<%= @shop %>";
20
- window.returnTo = "<%= params[:return_to] %>"
21
- </script>
22
-
23
23
  <%= javascript_include_tag('shopify_app/enable_cookies', crossorigin: 'anonymous', integrity: true) %>
24
24
  </head>
25
25
  <body>
@@ -5,6 +5,11 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <base target="_top">
7
7
  <title>Redirecting…</title>
8
+ <script src="https://unpkg.com/@shopify/app-bridge@^1"></script>
9
+ <script>
10
+ window.apiKey = "<%= ShopifyApp.configuration.api_key %>";
11
+ window.shopOrigin = "https://<%= current_shopify_domain %>";
12
+ </script>
8
13
  <%= render 'shopify_app/partials/layout_styles' %>
9
14
  <%= render 'shopify_app/partials/typography_styles' %>
10
15
  <%= render 'shopify_app/partials/card_styles' %>
@@ -1,10 +1,15 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="<%= I18n.locale %>">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <base target="_top">
7
7
  <title>Redirecting…</title>
8
+ <script src="https://unpkg.com/@shopify/app-bridge@^1"></script>
9
+ <script>
10
+ window.apiKey = "<%= ShopifyApp.configuration.api_key %>";
11
+ window.shopOrigin = "https://<%= current_shopify_domain %>";
12
+ </script>
8
13
  <%= javascript_include_tag('shopify_app/redirect', crossorigin: 'anonymous', integrity: true) %>
9
14
  </head>
10
15
  <body>
@@ -40,11 +40,10 @@ $ heroku create name
40
40
  4. Add ShopifyApp to Gemfile
41
41
  ----------------------------
42
42
 
43
- Run these commands to add the `shopify_app` Gem to your app:
43
+ Run this command to add the `shopify_app` Gem to your app:
44
44
 
45
45
  ```sh
46
- $ echo "gem 'shopify_app'" >> Gemfile
47
- $ bundle install
46
+ $ bundle add shopify_app
48
47
  ```
49
48
 
50
49
  **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'`
@@ -1,18 +1,18 @@
1
1
  Releasing ShopifyApp
2
2
 
3
3
  1. Check the Semantic Versioning page for info on how to version the new release: http://semver.org
4
- 2. Create a pull request with the following changes:
5
- * Update the version of ShopifyApp in lib/shopify_app/version.rb
6
- * Update the version of shopify_app in package.json
7
- * Add a CHANGELOG entry for the new release with the date
8
- * Change the title of the PR to something like: "Packaging for release X.Y.Z"
9
- 3. Merge your pull request
10
- 4. Pull from master so you have the latest version of the shopify_app
11
- 5. Tag the HEAD with the version (Leave REV blank for HEAD or provide a SHA)
12
- $ git tag vX.Y.Z
13
- 6. Push out your tags
14
- $ git push --tags
15
- 7. Use Shipit to build and push the gem
4
+ 1. Create a pull request with the following changes:
5
+ - Update the version of ShopifyApp in lib/shopify_app/version.rb
6
+ - Update the version of shopify_app in package.json
7
+ - Add a CHANGELOG entry for the new release with the date
8
+ - Change the title of the PR to something like: "Packaging for release X.Y.Z"
9
+ 1. Merge your pull request
10
+ 1. Checkout and pull from master so you have the latest version of the shopify_app
11
+ 1. Tag the HEAD with the version
12
+ ```bash
13
+ $ git tag -f vX.Y.Z && git push --tags --force
14
+ ```
15
+ 1. Use Shipit to build and push the gem
16
16
 
17
17
  If you see an error like 'You need to create the vX.Y.X tag first', clear GIT
18
18
  cache in Shipit settings
@@ -7,7 +7,7 @@ module ShopifyApp
7
7
  class AuthenticatedControllerGenerator < Rails::Generators::Base
8
8
  source_root File.expand_path('../templates', __FILE__)
9
9
 
10
- def create_home_controller
10
+ def create_authenticated_controller
11
11
  template('authenticated_controller.rb', 'app/controllers/authenticated_controller.rb')
12
12
  end
13
13
  end
@@ -6,21 +6,39 @@ module ShopifyApp
6
6
  class HomeControllerGenerator < Rails::Generators::Base
7
7
  source_root File.expand_path('../templates', __FILE__)
8
8
 
9
+ class_option :with_session_token, type: :boolean, default: false
10
+
9
11
  def create_home_controller
10
- template('home_controller.rb', 'app/controllers/home_controller.rb')
12
+ @with_session_token = options['with_session_token']
13
+
14
+ template(home_controller_template, 'app/controllers/home_controller.rb')
15
+ end
16
+
17
+ def create_products_controller
18
+ generate("shopify_app:products_controller") if with_session_token?
11
19
  end
12
20
 
13
21
  def create_home_index_view
14
- copy_file('index.html.erb', 'app/views/home/index.html.erb')
22
+ template('index.html.erb', 'app/views/home/index.html.erb')
15
23
  end
16
24
 
17
25
  def add_home_index_route
18
26
  route("root :to => 'home#index'")
19
27
  end
20
28
 
29
+ private
30
+
21
31
  def embedded_app?
22
32
  ShopifyApp.configuration.embedded_app?
23
33
  end
34
+
35
+ def with_session_token?
36
+ @with_session_token
37
+ end
38
+
39
+ def home_controller_template
40
+ with_session_token? ? 'unauthenticated_home_controller.rb' : 'home_controller.rb'
41
+ end
24
42
  end
25
43
  end
26
44
  end
@@ -1,21 +1,71 @@
1
- <h2>Products</h2>
1
+ <!DOCTYPE html>
2
+ <html lang="<%= I18n.locale %>">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <link
7
+ rel="stylesheet"
8
+ href="https://unpkg.com/@shopify/polaris@4.25.0/styles.min.css"
9
+ />
10
+ <% if @with_session_token %> <script>
11
+ document.addEventListener("DOMContentLoaded", async function() {
12
+ var SessionToken = window["app-bridge"].actions.SessionToken
13
+ var app = window.app;
2
14
 
3
- <ul>
4
- <% @products.each do |product| %>
5
- <li><%= link_to product.title, "https://#{@current_shopify_session.domain}/admin/products/#{product.id}", target: "_top" %></li>
6
- <% end %>
7
- </ul>
15
+ app.dispatch(
16
+ SessionToken.request(),
17
+ );
8
18
 
9
- <hr>
19
+ // Save a session token for future requests
20
+ window.sessionToken = await new Promise((resolve) => {
21
+ app.subscribe(SessionToken.ActionType.RESPOND, (data) => {
22
+ resolve(data.sessionToken || "");
23
+ });
24
+ });
10
25
 
11
- <h2>Webhooks</h2>
26
+ var fetchProducts = function() {
27
+ var headers = new Headers({ "Authorization": "Bearer " + window.sessionToken });
28
+ return fetch("/products", { headers })
29
+ .then(response => response.json())
30
+ .then(data => {
31
+ var products = data.products;
12
32
 
13
- <% if @webhooks.present? %>
14
- <ul>
15
- <% @webhooks.each do |webhook| %>
16
- <li><%= webhook.topic %> : <%= webhook.address %></li>
17
- <% end %>
18
- </ul>
19
- <% else %>
20
- <p>This app has not created any webhooks for this Shop. Add webhooks to your ShopifyApp initializer if you need webhooks</p>
21
- <% end %>
33
+ if (products === undefined || products.length == 0) {
34
+ document.getElementById("products").innerHTML = "<br>No products to display.";
35
+ } else {
36
+ var list = "";
37
+ products.forEach((product) => {
38
+ var link = `<a target="_top" href="https://<%%= @shop_origin %>/admin/products/${product.id}">`
39
+ list += "<li>" + link + product.title + "</a></li>";
40
+ });
41
+ document.getElementById("products").innerHTML = "<ul>" + list + "</ul>";
42
+ }
43
+ });
44
+ }();
45
+ });
46
+ </script>
47
+ <% end %> </head>
48
+ <body>
49
+ <h2>Products</h2>
50
+ <% if @with_session_token %> <div id="products"><br>Loading...</div><% else %>
51
+ <ul>
52
+ <%% @products.each do |product| %>
53
+ <li><%%= link_to product.title, "https://#{@current_shopify_session.domain}/admin/products/#{product.id}", target: "_top" %></li>
54
+ <%% end %>
55
+ </ul>
56
+
57
+ <hr>
58
+
59
+ <h2>Webhooks</h2>
60
+
61
+ <%% if @webhooks.present? %>
62
+ <ul>
63
+ <%% @webhooks.each do |webhook| %>
64
+ <li><%%= webhook.topic %> : <%%= webhook.address %></li>
65
+ <%% end %>
66
+ </ul>
67
+ <%% else %>
68
+ <p>This app has not created any webhooks for this Shop. Add webhooks to your ShopifyApp initializer if you need webhooks</p>
69
+ <%% end %><% end %>
70
+ </body>
71
+ </html>
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HomeController < ApplicationController
4
+ include ShopifyApp::EmbeddedApp
5
+ include ShopifyApp::RequireKnownShop
6
+
7
+ def index
8
+ @shop_origin = current_shopify_domain
9
+ end
10
+ end
@@ -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: (@current_shopify_session.domain if @current_shopify_session),
31
+ shop_origin: @shop_origin || (@current_shopify_session.domain if @current_shopify_session),
32
32
  debug: Rails.env.development?
33
33
  } ) %>
34
34