shopify_app 17.0.1 → 17.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug-report.md +63 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +1 -0
  4. data/.github/ISSUE_TEMPLATE/feature-request.md +33 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +17 -1
  6. data/.github/workflows/release.yml +6 -2
  7. data/CHANGELOG.md +21 -0
  8. data/CONTRIBUTING.md +76 -0
  9. data/Gemfile.lock +63 -63
  10. data/README.md +72 -593
  11. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +4 -0
  12. data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +32 -0
  13. data/app/controllers/shopify_app/callback_controller.rb +35 -4
  14. data/app/controllers/shopify_app/sessions_controller.rb +13 -4
  15. data/docs/Quickstart.md +15 -77
  16. data/docs/Upgrading.md +110 -0
  17. data/docs/shopify_app/authentication.md +124 -0
  18. data/docs/shopify_app/engine.md +82 -0
  19. data/docs/shopify_app/generators.md +127 -0
  20. data/docs/shopify_app/handling-access-scopes-changes.md +8 -0
  21. data/docs/shopify_app/script-tags.md +28 -0
  22. data/docs/shopify_app/session-repository.md +88 -0
  23. data/docs/shopify_app/testing.md +38 -0
  24. data/docs/shopify_app/webhooks.md +72 -0
  25. data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +2 -0
  26. data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +1 -0
  27. data/lib/generators/shopify_app/install/install_generator.rb +30 -1
  28. data/lib/generators/shopify_app/install/templates/omniauth.rb +1 -0
  29. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +20 -14
  30. data/lib/generators/shopify_app/install/templates/shopify_provider.rb.tt +8 -0
  31. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +27 -0
  32. data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_scopes_column.erb +5 -0
  33. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -1
  34. data/lib/generators/shopify_app/shopify_app_generator.rb +1 -1
  35. data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_access_scopes_column.erb +5 -0
  36. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -1
  37. data/lib/generators/shopify_app/user_model/user_model_generator.rb +27 -0
  38. data/lib/shopify_app.rb +10 -0
  39. data/lib/shopify_app/access_scopes/noop_strategy.rb +13 -0
  40. data/lib/shopify_app/access_scopes/shop_strategy.rb +24 -0
  41. data/lib/shopify_app/access_scopes/user_strategy.rb +41 -0
  42. data/lib/shopify_app/configuration.rb +22 -0
  43. data/lib/shopify_app/omniauth/omniauth_configuration.rb +64 -0
  44. data/lib/shopify_app/session/in_memory_shop_session_store.rb +9 -7
  45. data/lib/shopify_app/session/in_memory_user_session_store.rb +9 -7
  46. data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +58 -0
  47. data/lib/shopify_app/session/user_session_storage_with_scopes.rb +58 -0
  48. data/lib/shopify_app/utils.rb +12 -0
  49. data/lib/shopify_app/version.rb +1 -1
  50. data/package.json +1 -1
  51. data/shopify_app.gemspec +1 -1
  52. data/yarn.lock +70 -101
  53. metadata +27 -8
  54. data/.github/ISSUE_TEMPLATE.md +0 -19
  55. data/docs/install-on-dev-shop.png +0 -0
  56. data/docs/test-your-app.png +0 -0
  57. data/lib/generators/shopify_app/install/templates/shopify_provider.rb +0 -20
@@ -13,6 +13,10 @@ module ShopifyApp
13
13
  def redirect_to_splash_page
14
14
  splash_page_path = root_path(return_to: request.fullpath, shop: current_shopify_domain)
15
15
  redirect_to(splash_page_path)
16
+ rescue ShopifyApp::LoginProtection::ShopifyDomainNotFound => error
17
+ Rails.logger.warn("[ShopifyApp::EnsureAuthenticatedLinks] Redirecting to login: [#{error.class}] "\
18
+ "Could not determine current shop domain")
19
+ redirect_to(ShopifyApp.configuration.login_url)
16
20
  end
17
21
 
18
22
  def missing_expected_jwt?
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyApp
4
+ module ShopAccessScopesVerification
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_action :login_on_scope_changes
9
+ end
10
+
11
+ protected
12
+
13
+ def login_on_scope_changes
14
+ redirect_to(shop_login) if scopes_mismatch?
15
+ end
16
+
17
+ private
18
+
19
+ def scopes_mismatch?
20
+ ShopifyApp.configuration.shop_access_scopes_strategy.update_access_scopes?(current_shopify_domain)
21
+ end
22
+
23
+ def current_shopify_domain
24
+ return if params[:shop].blank?
25
+ ShopifyApp::Utils.sanitize_shop_domain(params[:shop])
26
+ end
27
+
28
+ def shop_login
29
+ ShopifyApp::Utils.shop_login_url(shop: params[:shop], return_to: request.fullpath)
30
+ end
31
+ end
32
+ end
@@ -65,14 +65,31 @@ module ShopifyApp
65
65
  end
66
66
  end
67
67
 
68
+ # Override user_session_by_cookie from LoginProtection to bypass allow_cookie_authentication
69
+ # setting check because session cookies are justified at top level
70
+ def user_session_by_cookie
71
+ return unless session[:user_id].present?
72
+ ShopifyApp::SessionRepository.retrieve_user_session(session[:user_id])
73
+ end
74
+
68
75
  def start_user_token_flow?
69
76
  if jwt_request?
70
77
  false
71
78
  else
72
- ShopifyApp::SessionRepository.user_storage.present? && user_session.blank?
79
+ return false unless ShopifyApp::SessionRepository.user_storage.present?
80
+ update_user_access_scopes?
73
81
  end
74
82
  end
75
83
 
84
+ def update_user_access_scopes?
85
+ return true if user_session.blank?
86
+ user_access_scopes_strategy.update_access_scopes?(user_id: session[:user_id])
87
+ end
88
+
89
+ def user_access_scopes_strategy
90
+ ShopifyApp.configuration.user_access_scopes_strategy
91
+ end
92
+
76
93
  def jwt_request?
77
94
  jwt_shopify_domain || jwt_shopify_user_id
78
95
  end
@@ -89,6 +106,14 @@ module ShopifyApp
89
106
  auth_hash.uid
90
107
  end
91
108
 
109
+ def offline_access_token
110
+ ShopifyApp::SessionRepository.retrieve_shop_session_by_shopify_domain(shop_name)&.token
111
+ end
112
+
113
+ def online_access_token
114
+ ShopifyApp::SessionRepository.retrieve_user_session_by_shopify_user_id(associated_user_id)&.token
115
+ end
116
+
92
117
  def associated_user
93
118
  return unless auth_hash.dig('extra', 'associated_user').present?
94
119
 
@@ -103,6 +128,11 @@ module ShopifyApp
103
128
  auth_hash['credentials']['token']
104
129
  end
105
130
 
131
+ def access_scopes
132
+ return unless auth_hash['extra']['scope']
133
+ auth_hash['extra']['scope']
134
+ end
135
+
106
136
  def reset_session_options
107
137
  request.session_options[:renew] = true
108
138
  session.delete(:_csrf_token)
@@ -112,7 +142,8 @@ module ShopifyApp
112
142
  session_store = ShopifyAPI::Session.new(
113
143
  domain: shop_name,
114
144
  token: token,
115
- api_version: ShopifyApp.configuration.api_version
145
+ api_version: ShopifyApp.configuration.api_version,
146
+ access_scopes: access_scopes
116
147
  )
117
148
 
118
149
  session[:shopify_user] = associated_user
@@ -132,7 +163,7 @@ module ShopifyApp
132
163
 
133
164
  WebhooksManager.queue(
134
165
  shop_name,
135
- shop_session&.token || user_session.token,
166
+ offline_access_token || online_access_token,
136
167
  ShopifyApp.configuration.webhooks
137
168
  )
138
169
  end
@@ -142,7 +173,7 @@ module ShopifyApp
142
173
 
143
174
  ScripttagsManager.queue(
144
175
  shop_name,
145
- shop_session&.token || user_session.token,
176
+ offline_access_token || online_access_token,
146
177
  ShopifyApp.configuration.scripttags
147
178
  )
148
179
  end
@@ -92,9 +92,18 @@ module ShopifyApp
92
92
  end
93
93
  end
94
94
 
95
+ # Override shop_session_by_cookie from LoginProtection to bypass allow_cookie_authentication
96
+ # setting check because session cookies are justified at top level
97
+ def shop_session_by_cookie
98
+ return unless session[:shop_id].present?
99
+ ShopifyApp::SessionRepository.retrieve_shop_session(session[:shop_id])
100
+ end
101
+
95
102
  # rubocop:disable Lint/SuppressedException
96
103
  def set_user_tokens_option
97
- if shop_session.blank?
104
+ current_shop_session = shop_session
105
+
106
+ if current_shop_session.blank?
98
107
  session[:user_tokens] = false
99
108
  return
100
109
  end
@@ -102,9 +111,9 @@ module ShopifyApp
102
111
  session[:user_tokens] = ShopifyApp::SessionRepository.user_storage.present?
103
112
 
104
113
  ShopifyAPI::Session.temp(
105
- domain: shop_session.domain,
106
- token: shop_session.token,
107
- api_version: shop_session.api_version
114
+ domain: current_shop_session.domain,
115
+ token: current_shop_session.token,
116
+ api_version: current_shop_session.api_version
108
117
  ) do
109
118
  ShopifyAPI::Metafield.find(:token_validity_bogus_check)
110
119
  end
data/docs/Quickstart.md CHANGED
@@ -1,93 +1,31 @@
1
- Quickstart
2
- ==========
1
+ # Quickstart
3
2
 
4
- Get started building and deploying a new Shopify App to Heroku in just a few minutes.
5
- This guide assumes you have Ruby, Rails and PostgreSQL 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)
3
+ This guide assumes you have completed the steps to create a new Rails app using the Shopify App gem found in the [*Usage*](/README.md#usage) section of the project's [*README*](/README.md).
6
4
 
7
- 1. New Rails App (with postgres)
8
- --------------------------------
5
+ #### Table of contents
9
6
 
10
- To create a new Rails app and use this generator, open your terminal and run the following commands:
7
+ [Make your app available to the internet](#make-your-app-available-to-the-internet)
11
8
 
12
- ```sh
13
- $ rails new test-app --database=postgresql
14
- $ cd test-app
15
- $ git init
16
- $ git add .
17
- $ git commit -m 'new rails app'
18
- ```
19
-
20
- 2. Create a new Heroku app
21
- --------------------------
22
-
23
- 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/).
24
-
25
- 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:
26
-
27
- CLI:
28
- ```sh
29
- $ heroku create name
30
- ```
31
-
32
- 3. Create a new App in the Shopify Partner dashboard
33
- -----------------------------------------
34
- * 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)
35
- [https://app.shopify.com/services/partners/api_clients](https://app.shopify.com/services/partners/api_clients)
36
- * Set the callback url to `https://<appname>.herokuapp.com/`
37
- * Choose an embedded app
38
- * Set the app's `redirect_uri` to `https://<appname>.herokuapp.com/auth/shopify/callback`
39
-
40
- 4. Add ShopifyApp to Gemfile
41
- ----------------------------
42
-
43
- Run this command to add the `shopify_app` Gem to your app:
9
+ [Use Shopify App Bridge to embed your app in the Shopify Admin](#use-shopify-app-bridge-to-embed-your-app-in-the-shopify-admin)
44
10
 
45
- ```sh
46
- $ bundle add shopify_app
47
- ```
11
+ ## Make your app available to the internet
48
12
 
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'`
13
+ Your local app needs to be accessible from the public Internet in order to install it on a Shopify store, to use the [App Proxy Controller](/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb) or receive [webhooks](/docs/shopify_app/webhooks.md).
50
14
 
51
- 5. Run the ShopifyApp generator
52
- -------------------------------
15
+ Use a tunneling service like [ngrok](https://ngrok.com/), [Beeceptor](https://beeceptor.com/), [Mockbin](http://mockbin.org/), or [Hookbin](https://hookbin.com/) to make your development environment accessible to the internet.
53
16
 
54
- Generate the code for your app by running these commands:
17
+ For example with [ngrok](https://ngrok.com/), run this command to set up a tunnel proxy to Rails' default port:
55
18
 
56
19
  ```sh
57
- # Use the keys from your app you created in the partners area
58
- $ rails generate shopify_app
59
- $ git add .
60
- $ git commit -m 'generated shopify app'
20
+ ngrok http 3000
61
21
  ```
62
22
 
63
- Your API key and secret are read from environment variables. Refer to the main
64
- README for further details on how to set this up.
65
-
66
- 6. Deploy your app
67
- ---------
68
-
69
- Once you've generated your app, push it into your Heroku environment to see it up and running:
70
- ```sh
71
- $ git push heroku
72
- $ heroku run rake db:migrate
73
- ```
74
-
75
- 7. Install the App!
76
- -------------------
77
-
78
- 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).
79
-
80
- ##### 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.
81
-
82
- 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.
83
-
84
- ![Installing an app on the partners dashboard dropdown](/docs/install-on-dev-shop.png)
23
+ See the [*Embed the app in Shopify*](https://shopify.dev/tutorials/build-rails-react-app-that-uses-app-bridge-authentication#embed-the-app-in-shopify) section of [*Build a Shopify app with Rails, React, and App Bridge*](https://shopify.dev/tutorials/build-rails-react-app-that-uses-app-bridge-authentication) to learn more.
85
24
 
86
- ### OR
25
+ ## Use Shopify App Bridge to embed your app in the Shopify Admin
87
26
 
88
- ![Installing an app on the partners dashboard card](/docs/test-your-app.png)
27
+ A basic example of using [*Shopify App Bridge*](https://shopify.dev/tools/app-bridge) is included in the install generator. An instance Shopify App Bridge is automatically initialized in [shopify_app.js](https://github.com/Shopify/shopify_app/blob/master/lib/generators/shopify_app/install/templates/shopify_app.js).
89
28
 
90
- 8. Great work!
91
- -------------------
29
+ The [flash_messages.js](https://github.com/Shopify/shopify_app/blob/master/lib/generators/shopify_app/install/templates/flash_messages.js) file converts Rails [flash messages](https://api.rubyonrails.org/classes/ActionDispatch/Flash.html) to App Bridge Toast actions automatically. By default, this library is included via [unpkg in the embedded_app layout](https://github.com/Shopify/shopify_app/blob/master/lib/generators/shopify_app/install/templates/embedded_app.html.erb#L27).
92
30
 
93
- 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)
31
+ For more advanced uses it is recommended to [install App Bridge via npm or yarn](https://help.shopify.com/en/api/embedded-apps/app-bridge/getting-started#set-up-shopify-app-bridge-in-your-app).
data/docs/Upgrading.md ADDED
@@ -0,0 +1,110 @@
1
+ # Upgrading
2
+
3
+ This file documents important changes needed to upgrade your app's Shopify App version to a new major version.
4
+
5
+ #### Table of contents
6
+
7
+ [Upgrading to `v13.0.0`](#upgrading-to-v1300)
8
+
9
+ [Upgrading to `v11.7.0`](#upgrading-to-v1170)
10
+
11
+ [Upgrading from `v8.6` to `v9.0.0`](#upgrading-from-v86-to-v900)
12
+
13
+ ## Upgrading to `v13.0.0`
14
+
15
+ Version 13.0.0 adds the ability to use both user and shop sessions, concurrently. This however involved a large
16
+ change to how session stores work. Here are the steps to migrate to 13.x
17
+
18
+ ### Changes to `config/initializers/shopify_app.rb`
19
+
20
+ - *REMOVE* `config.per_user_tokens = [true|false]` this is no longer needed
21
+ - *CHANGE* `config.session_repository = 'Shop'` To `config.shop_session_repository = 'Shop'`
22
+ - *ADD (optional)* User Session Storage `config.user_session_repository = 'User'`
23
+
24
+ ### Shop Model Changes (normally `app/models/shop.rb`)
25
+
26
+ - *CHANGE* `include ShopifyApp::SessionStorage` to `include ShopifyApp::ShopSessionStorage`
27
+
28
+ ### Changes to the @shop_session instance variable (normally in `app/controllers/*.rb`)
29
+
30
+ - *CHANGE* if you are using shop sessions, `@shop_session` will need to be changed to `@current_shopify_session`.
31
+
32
+ ### Changes to Rails `session`
33
+
34
+ - *CHANGE* `session[:shopify]` is no longer set. Use `session[:user_id]` if your app uses user based tokens, or `session[:shop_id]` if your app uses shop based tokens.
35
+
36
+ ### Changes to `ShopifyApp::LoginProtection`
37
+
38
+ `ShopifyApp::LoginProtection`
39
+
40
+ - CHANGE if you are using `ShopifyApp::LoginProtection#shopify_session` in your code, it will need to be
41
+ changed to `ShopifyApp::LoginProtection#activate_shopify_session`
42
+ - CHANGE if you are using `ShopifyApp::LoginProtection#clear_shop_session` in your code, it will need to be
43
+ changed to `ShopifyApp::LoginProtection#clear_shopify_session`
44
+
45
+ ### Notes
46
+ You do not need a user model; a shop session is fine for most applications.
47
+
48
+ ---
49
+
50
+ ## Upgrading to `v11.7.0`
51
+
52
+ ### Session storage method signature breaking change
53
+ If you override `def self.store(auth_session)` method in your session storage model (e.g. Shop), the method signature has changed to `def self.store(auth_session, *args)` in order to support user-based token storage. Please update your method signature to include the second argument.
54
+
55
+ ---
56
+
57
+ ## Upgrading from `v8.6` to `v9.0.0`
58
+
59
+ ### Configuration change
60
+
61
+ Add an API version configuration in `config/initializers/shopify_app.rb`
62
+ Set this to the version you want to run against by default. See [Shopify API docs](https://help.shopify.com/en/api/versioning) for versions available.
63
+ ```ruby
64
+ config.api_version = '2019-04'
65
+ ```
66
+
67
+ ### Session storage change
68
+
69
+ You will need to add an `api_version` method to your session storage object. The default implementation for this is.
70
+ ```ruby
71
+ def api_version
72
+ ShopifyApp.configuration.api_version
73
+ end
74
+ ```
75
+
76
+ ### Generated file change
77
+
78
+ `embedded_app.html.erb` the usage of `shop_session.url` needs to be changed to `shop_session.domain`
79
+ ```erb
80
+ <script type="text/javascript">
81
+ ShopifyApp.init({
82
+ apiKey: "<%= ShopifyApp.configuration.api_key %>",
83
+
84
+ shopOrigin: "<%= "https://#{ @shop_session.url }" if @shop_session %>",
85
+
86
+ debug: false,
87
+ forceRedirect: true
88
+ });
89
+ </script>
90
+ ```
91
+ is changed to
92
+ ```erb
93
+ <script type="text/javascript">
94
+ ShopifyApp.init({
95
+ apiKey: "<%= ShopifyApp.configuration.api_key %>",
96
+
97
+ shopOrigin: "<%= "https://#{ @shop_session.domain }" if @shop_session %>",
98
+
99
+ debug: false,
100
+ forceRedirect: true
101
+ });
102
+ </script>
103
+ ```
104
+
105
+ ### ShopifyAPI changes
106
+
107
+ You will need to also follow the ShopifyAPI [upgrade guide](https://github.com/Shopify/shopify_api/blob/master/README.md#-breaking-change-notice-for-version-700-) to ensure your app is ready to work with API versioning.
108
+
109
+ [dashboard]:https://partners.shopify.com
110
+ [app-bridge]:https://help.shopify.com/en/api/embedded-apps/app-bridge
@@ -0,0 +1,124 @@
1
+ # Authentication
2
+
3
+ The Shopify App gem implements [OAuth 2.0](https://shopify.dev/tutorials/authenticate-with-oauth) to get [access tokens](https://shopify.dev/concepts/about-apis/authentication#api-access-modes). These are used to authenticate requests made by the app to the Shopify API.
4
+
5
+ By default, the gem generates an embedded app frontend that uses [Shopify App Bridge](https://shopify.dev/tools/app-bridge) to fetch [session tokens](https://shopify.dev/concepts/apps/building-embedded-apps-using-session-tokens). Session tokens are used by the embedded app to make authenticated requests to the app backend.
6
+
7
+ See [*Authenticate an embedded app using session tokens*](https://shopify.dev/tutorials/authenticate-your-app-using-session-tokens) to learn more.
8
+
9
+ > ⚠️ Be sure you understand the differences between the types of authentication schemes before reading this guide.
10
+
11
+ #### Table of contents
12
+
13
+ [OAuth callback](#oauth-callback)
14
+
15
+ [Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
16
+
17
+ [Rotate API credentials](#rotate-api-credentials)
18
+
19
+ [Available authentication mixins](#available-authentication-mixins)
20
+ * [`ShopifyApp::Authenticated`](#shopifyappauthenticated)
21
+ * [`ShopifyApp::EnsureAuthenticatedLinks`](#shopifyappensureauthenticatedlinks)
22
+
23
+ ## OAuth callback
24
+
25
+ >️ **Note:** In Shopify App version 8.4.0, we have extracted the callback logic in its own controller. If you are upgrading from a version older than 8.4.0 the callback action and related helper methods were defined in `ShopifyApp::SessionsController` ==> you will have to extend `ShopifyApp::CallbackController` instead and port your logic to the new controller.
26
+
27
+ Upon completing the OAuth flow, Shopify calls the app at the `callback_path`. If the app needs to do some extra work, it can define and configure the route to a custom callback controller, inheriting from `ShopifyApp::CallbackController` and hook into or override any of the defined helper methods. The default callback controller already provides the following behaviour:
28
+ * Logging into the shop and resetting the session
29
+ * [Installing Webhooks](/docs/shopify_app/webhooks.md)
30
+ * [Setting Scripttags](/docs/shopify_app/script-tags.md)
31
+ * [Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
32
+ * Redirecting to the return address
33
+
34
+ ## Run jobs after the OAuth flow
35
+
36
+ See [`ShopifyApp::AfterAuthenticateJob`](/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb).
37
+
38
+ If your app needs to perform specific actions after the user is authenticated successfully (i.e. every time a new session is created), ShopifyApp can queue or run a job of your choosing (note that we already provide support for automatically creating Webhooks and Scripttags). To configure the after authenticate job, update your initializer as follows:
39
+
40
+ ```ruby
41
+ ShopifyApp.configure do |config|
42
+ config.after_authenticate_job = { job: "Shopify::AfterAuthenticateJob" }
43
+ end
44
+ ```
45
+
46
+ The job can be configured as either a class or a class name string.
47
+
48
+ If you need the job to run synchronously add the `inline` flag:
49
+
50
+ ```ruby
51
+ ShopifyApp.configure do |config|
52
+ config.after_authenticate_job = { job: Shopify::AfterAuthenticateJob, inline: true }
53
+ end
54
+ ```
55
+
56
+ We've also provided a generator which creates a skeleton job and updates the initializer for you:
57
+
58
+ ```
59
+ bin/rails g shopify_app:add_after_authenticate_job
60
+ ```
61
+
62
+ If you want to perform that action only once, e.g. send a welcome email to the user when they install the app, you should make sure that this action is idempotent, meaning that it won't have an impact if run multiple times.
63
+
64
+ ## Rotate API credentials
65
+
66
+ 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).
67
+
68
+ 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:
69
+
70
+ ```ruby
71
+ config.secret = Rails.application.secrets.shopify_secret
72
+ config.old_secret = Rails.application.secrets.old_shopify_secret
73
+ ```
74
+
75
+ We've provided a generator which creates the job and an example rake task:
76
+
77
+ ```sh
78
+ bin/rails g shopify_app:rotate_shopify_token_job
79
+ ```
80
+
81
+ 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.
82
+
83
+ ```ruby
84
+ strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
85
+ ```
86
+
87
+ > **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/initializers/omniauth.rb`:
88
+
89
+ ## Available authentication mixins
90
+
91
+ ### `ShopifyApp::Authenticated`
92
+
93
+ The engine provides a [`ShopifyApp::Authenticated`](/app/controllers/concerns/shopify_app/authenticated.rb) concern which should be included in any controller that is intended to be behind Shopify OAuth. It adds `before_action`s to ensure that the user is authenticated and will redirect to the Shopify login page if not. It is best practice to include this concern in a base controller inheriting from your `ApplicationController`, from which all controllers that require Shopify authentication inherit.
94
+
95
+ *Example:*
96
+
97
+ ```rb
98
+ class AuthenticatedController < ApplicationController
99
+ include ShopifyApp::Authenticated
100
+ end
101
+
102
+ class ApiController < AuthenticatedController
103
+ # Actions in this controller are protected
104
+ end
105
+ ```
106
+
107
+ For backwards compatibility, the engine still provides a controller called `ShopifyApp::AuthenticatedController` which includes the `ShopifyApp::Authenticated` concern. Note that it inherits directly from `ActionController::Base`, so you will not be able to share functionality between it and your application's `ApplicationController`.
108
+
109
+ ### `ShopifyApp::EnsureAuthenticatedLinks`
110
+
111
+ The [`ShopifyApp::EnsureAuthenticatedLinks`](/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb) concern helps authenticate users that access protected pages of your app directly.
112
+
113
+ Include this concern in your app's `AuthenticatedController` if your app uses session tokens with [Turbolinks](https://github.com/turbolinks/turbolinks). It adds a `before_action` filter that detects whether a session token is present or not. If a session token is not found, the user is redirected to your app's splash page path (`root_path`) along with `return_to` and `shop` parameters.
114
+
115
+ *Example:*
116
+
117
+ ```rb
118
+ class AuthenticatedController < ApplicationController
119
+ include ShopifyApp::EnsureAuthenticatedLinks
120
+ include ShopifyApp::Authenticated
121
+ end
122
+ ```
123
+
124
+ See [Authenticate server-side rendered embedded apps using Rails and Turbolinks](https://shopify.dev/tutorials/authenticate-server-side-rendered-embedded-apps-using-rails-and-turbolinks) for more information.