shopify_app 21.6.0 → 22.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +23 -18
  4. data/.github/workflows/build.yml +2 -2
  5. data/.github/workflows/release.yml +1 -1
  6. data/.github/workflows/rubocop.yml +1 -2
  7. data/.nvmrc +1 -1
  8. data/.rubocop.yml +0 -1
  9. data/CHANGELOG.md +115 -0
  10. data/CODE_OF_CONDUCT.md +46 -0
  11. data/CONTRIBUTING.md +1 -6
  12. data/Gemfile.lock +99 -96
  13. data/README.md +47 -2
  14. data/app/assets/javascripts/shopify_app/redirect.js +3 -10
  15. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +5 -1
  16. data/app/controllers/concerns/shopify_app/ensure_has_session.rb +11 -5
  17. data/app/controllers/concerns/shopify_app/ensure_installed.rb +10 -4
  18. data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +5 -1
  19. data/app/controllers/shopify_app/callback_controller.rb +39 -18
  20. data/app/controllers/shopify_app/sessions_controller.rb +25 -4
  21. data/app/views/shopify_app/layouts/app_bridge.html.erb +17 -0
  22. data/app/views/shopify_app/sessions/patch_shopify_id_token.html.erb +0 -0
  23. data/app/views/shopify_app/shared/redirect.html.erb +10 -1
  24. data/config/locales/cs.yml +0 -18
  25. data/config/locales/da.yml +0 -15
  26. data/config/locales/de.yml +0 -17
  27. data/config/locales/en.yml +0 -11
  28. data/config/locales/es.yml +0 -17
  29. data/config/locales/fi.yml +0 -15
  30. data/config/locales/fr.yml +0 -18
  31. data/config/locales/it.yml +0 -16
  32. data/config/locales/ja.yml +0 -12
  33. data/config/locales/ko.yml +0 -14
  34. data/config/locales/nb.yml +0 -16
  35. data/config/locales/nl.yml +0 -16
  36. data/config/locales/pl.yml +0 -16
  37. data/config/locales/pt-BR.yml +0 -16
  38. data/config/locales/pt-PT.yml +0 -17
  39. data/config/locales/sv.yml +0 -16
  40. data/config/locales/th.yml +0 -15
  41. data/config/locales/tr.yml +0 -17
  42. data/config/locales/vi.yml +0 -17
  43. data/config/locales/zh-CN.yml +0 -11
  44. data/config/locales/zh-TW.yml +0 -11
  45. data/config/routes.rb +2 -1
  46. data/docs/Quickstart.md +9 -2
  47. data/docs/Troubleshooting.md +0 -23
  48. data/docs/Upgrading.md +64 -1
  49. data/docs/shopify_app/authentication.md +179 -58
  50. data/docs/shopify_app/controller-concerns.md +53 -12
  51. data/docs/shopify_app/generators.md +2 -2
  52. data/docs/shopify_app/sessions.md +358 -0
  53. data/docs/shopify_app/webhooks.md +88 -11
  54. data/karma.conf.js +6 -4
  55. data/lib/generators/shopify_app/add_declarative_webhook/add_declarative_webhook_generator.rb +53 -0
  56. data/lib/generators/shopify_app/add_declarative_webhook/templates/webhook_controller.rb.tt +13 -0
  57. data/lib/generators/shopify_app/add_declarative_webhook/templates/webhook_job.rb.tt +15 -0
  58. data/lib/generators/shopify_app/{add_gdpr_jobs/add_gdpr_jobs_generator.rb → add_privacy_jobs/add_privacy_jobs_generator.rb} +1 -1
  59. data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +6 -1
  60. data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +1 -0
  61. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +5 -2
  62. data/lib/generators/shopify_app/shopify_app_generator.rb +1 -1
  63. data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_expires_at_column.erb +5 -0
  64. data/lib/generators/shopify_app/user_model/user_model_generator.rb +20 -0
  65. data/lib/shopify_app/admin_api/with_token_refetch.rb +27 -0
  66. data/lib/shopify_app/auth/post_authenticate_tasks.rb +48 -0
  67. data/lib/shopify_app/auth/token_exchange.rb +73 -0
  68. data/lib/shopify_app/configuration.rb +69 -1
  69. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +1 -1
  70. data/lib/shopify_app/controller_concerns/csrf_protection.rb +2 -1
  71. data/lib/shopify_app/controller_concerns/embedded_app.rb +42 -3
  72. data/lib/shopify_app/controller_concerns/ensure_billing.rb +14 -3
  73. data/lib/shopify_app/controller_concerns/frame_ancestors.rb +1 -1
  74. data/lib/shopify_app/controller_concerns/localization.rb +11 -8
  75. data/lib/shopify_app/controller_concerns/login_protection.rb +34 -38
  76. data/lib/shopify_app/controller_concerns/redirect_for_embedded.rb +5 -0
  77. data/lib/shopify_app/controller_concerns/sanitized_params.rb +4 -0
  78. data/lib/shopify_app/controller_concerns/token_exchange.rb +111 -0
  79. data/lib/shopify_app/controller_concerns/with_shopify_id_token.rb +48 -0
  80. data/lib/shopify_app/engine.rb +5 -11
  81. data/lib/shopify_app/managers/webhooks_manager.rb +6 -2
  82. data/lib/shopify_app/middleware/jwt_middleware.rb +13 -9
  83. data/lib/shopify_app/session/in_memory_user_session_store.rb +1 -1
  84. data/lib/shopify_app/session/jwt.rb +9 -0
  85. data/lib/shopify_app/session/session_repository.rb +49 -8
  86. data/lib/shopify_app/session/shop_session_storage.rb +4 -0
  87. data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +4 -0
  88. data/lib/shopify_app/session/user_session_storage.rb +4 -0
  89. data/lib/shopify_app/session/user_session_storage_with_scopes.rb +25 -0
  90. data/lib/shopify_app/test_helpers/shopify_session_helper.rb +1 -0
  91. data/lib/shopify_app/utils.rb +14 -1
  92. data/lib/shopify_app/version.rb +1 -1
  93. data/lib/shopify_app.rb +9 -3
  94. data/package.json +5 -6
  95. data/shopify_app.gemspec +4 -4
  96. data/yarn.lock +2134 -3905
  97. metadata +51 -60
  98. data/.github/workflows/stale.yml +0 -43
  99. data/app/assets/images/storage_access.svg +0 -1
  100. data/app/assets/javascripts/shopify_app/app_bridge_3.1.1.js +0 -10
  101. data/app/assets/javascripts/shopify_app/app_bridge_redirect.js +0 -22
  102. data/app/assets/javascripts/shopify_app/app_bridge_utils_3.1.1.js +0 -1
  103. data/app/controllers/concerns/shopify_app/authenticated.rb +0 -17
  104. data/app/controllers/concerns/shopify_app/require_known_shop.rb +0 -16
  105. data/docs/shopify_app/script-tags.md +0 -28
  106. data/docs/shopify_app/session-repository.md +0 -79
  107. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +0 -42
  108. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +0 -63
  109. data/lib/shopify_app/controller_concerns/itp.rb +0 -50
  110. data/lib/shopify_app/jobs/scripttags_manager_job.rb +0 -16
  111. data/lib/shopify_app/managers/scripttags_manager.rb +0 -85
  112. /data/lib/generators/shopify_app/{add_gdpr_jobs → add_privacy_jobs}/templates/customers_data_request_job.rb.tt +0 -0
  113. /data/lib/generators/shopify_app/{add_gdpr_jobs → add_privacy_jobs}/templates/customers_redact_job.rb.tt +0 -0
  114. /data/lib/generators/shopify_app/{add_gdpr_jobs → add_privacy_jobs}/templates/shop_redact_job.rb.tt +0 -0
@@ -3,19 +3,3 @@ pt-BR:
3
3
  logged_out: Você saiu.
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
- enable_cookies_heading: Habilitar cookies de %{app}
7
- enable_cookies_body: Você precisa habilitar manualmente os cookies neste navegador
8
- para usar %{app} dentro da Shopify.
9
- enable_cookies_footer: Os cookies permitem que o app o autentique armazenando temporariamente
10
- suas preferências e dados pessoais. Eles expiram depois de 30 dias.
11
- enable_cookies_action: Habilitar cookies
12
- top_level_interaction_heading: Seu navegador precisa autenticar %{app}
13
- top_level_interaction_body: Seu navegador exige que apps como o %{app} consultem
14
- você sobre o acesso a cookies antes que a Shopify os abra.
15
- top_level_interaction_action: Continuar
16
- request_storage_access_heading: "%{app} precisa acessar cookies"
17
- request_storage_access_body: Isso permite que o app autentique você armazenando
18
- temporariamente seus dados pessoais. Clique em continuar e permita os cookies
19
- para usar o app.
20
- request_storage_access_footer: Os cookies expiram depois de 30 dias.
21
- request_storage_access_action: Continuar
@@ -3,20 +3,3 @@ pt-PT:
3
3
  logged_out: Terminou a sessão com sucesso
4
4
  could_not_log_in: Não foi possível iniciar sessão na loja da Shopify
5
5
  invalid_shop_url: Domínio de loja inválido
6
- enable_cookies_heading: Ativar cookies de %{app}
7
- enable_cookies_body: Tem de ativar manualmente os cookies neste navegador para utilizar
8
- %{app} dentro da Shopify.
9
- enable_cookies_footer: Os cookies permitem que a aplicação o autentique armazenando
10
- temporariamente as suas preferências e informações pessoais. Expiram ao fim de
11
- 30 dias.
12
- enable_cookies_action: Ativar cookies
13
- top_level_interaction_heading: O seu navegador tem de autenticar %{app}
14
- top_level_interaction_body: O seu navegador exige que aplicações como %{app} lhe
15
- solicitem o acesso de cookies, antes que a Shopify as possa abrir.
16
- top_level_interaction_action: Continuar
17
- request_storage_access_heading: "%{app} tem de aceder a cookies"
18
- request_storage_access_body: Isto permite que a aplicação o autentique armazenando
19
- temporariamente as suas informações pessoais. Clique em continuar e permita os
20
- cookies para utilizar a aplicação.
21
- request_storage_access_footer: Os cookies expiram ao fim de 30 dias.
22
- request_storage_access_action: Continuar
@@ -3,19 +3,3 @@ sv:
3
3
  logged_out: Har loggats ut
4
4
  could_not_log_in: Det gick inte att logga in i Shopify-butiken
5
5
  invalid_shop_url: Ogiltig butiksdomän
6
- enable_cookies_heading: Aktivera cookies från %{app}
7
- enable_cookies_body: Du måste aktivera cookies manuellt i den här webbläsaren för
8
- att kunna använda %{app} inom Shopify.
9
- enable_cookies_footer: Cookies låter appen autentisera dig genom att tillfälligt
10
- lagra dina inställningar och personuppgifter. De upphör efter 30 dagar.
11
- enable_cookies_action: Aktivera cookies
12
- top_level_interaction_heading: Din webbläsare måste verifiera %{app}
13
- top_level_interaction_body: Din webbläsare kräver att appar som %{app} frågar dig
14
- om tillgång till cookies innan Shopify kan öppna den för dig.
15
- top_level_interaction_action: Fortsätt
16
- request_storage_access_heading: "%{app} behöver tillgång till cookies"
17
- request_storage_access_body: Detta gör det möjligt för appen att autentisera dig
18
- genom att tillfälligt lagra din personliga information. Klicka på fortsätt och
19
- tillåta cookies att använda appen.
20
- request_storage_access_footer: Cookies upphör efter 30 dagar.
21
- request_storage_access_action: Fortsätt
@@ -3,18 +3,3 @@ th:
3
3
  logged_out: ออกจากระบบสำเร็จ
4
4
  could_not_log_in: ไม่สามารถเข้าสู่ระบบร้านค้า Shopify ได้
5
5
  invalid_shop_url: โดเมนร้านค้าไม่ถูกต้อง
6
- enable_cookies_heading: เปิดใช้คุกกี้จาก %{app}
7
- enable_cookies_body: คุณต้องเปิดใช้คุกกี้ด้วยตนเองในเบราว์เซอร์นี้เพื่อใช้งาน %{app}
8
- ภายใน Shopify
9
- enable_cookies_footer: คุกกี้ช่วยให้แอปตรวจสอบความถูกต้องของคุณด้วยการจัดเก็บความชื่นชอบและข้อมูลส่วนตัวของคุณชั่วคราว
10
- คุกกี้จะหมดอายุหลังจาก 30 วัน
11
- enable_cookies_action: เปิดใช้คุกกี้
12
- top_level_interaction_heading: เบราว์เซอร์ของคุณต้องรับรองความถูกต้องของ %{app}
13
- top_level_interaction_body: เบราว์เซอร์ของคุณต้องการแอปอย่าง %{app} เพื่อขอให้คุณเข้าถึงคุกกี้ก่อนที่
14
- Shopify จะสามารถเปิดมันให้คุณได้
15
- top_level_interaction_action: ดำเนินการต่อ
16
- request_storage_access_heading: "%{app} ต้องการสิทธิ์การเข้าถึงคุกกี้"
17
- request_storage_access_body: สิ่งนี้ช่วยให้แอปตรวจสอบความถูกต้องของคุณด้วยการจัดเก็บข้อมูลส่วนตัวของคุณชั่วคราว
18
- คลิกดำเนินการต่อและอนุญาตให้คุกกี้ใช้แอป
19
- request_storage_access_footer: คุกกี้จะหมดอายุหลังจาก 30 วัน
20
- request_storage_access_action: ดำเนินการต่อ
@@ -3,20 +3,3 @@ tr:
3
3
  logged_out: Oturum başarıyla kapatıldı
4
4
  could_not_log_in: Shopify mağazasında oturum açılamadı
5
5
  invalid_shop_url: Geçersiz mağaza alan adı
6
- enable_cookies_heading: "%{app} uygulamasından çerezleri etkinleştir"
7
- enable_cookies_body: "%{app} uygulamasını Shopify içinde kullanabilmek için bu tarayıcıda
8
- çerezleri manuel olarak etkinleştirmelisiniz."
9
- enable_cookies_footer: Çerezler, tercihlerinizi ve kişisel bilgilerinizi geçici
10
- olarak saklayıp uygulamanın kimliğinizi doğrulamasına imkan tanır. Çerezlerin
11
- süresi 30 gün sonra sonra sona erer.
12
- enable_cookies_action: Çerezleri etkinleştir
13
- top_level_interaction_heading: Tarayıcınızın %{app} kimliğini doğrulaması gerekiyor
14
- top_level_interaction_body: Tarayıcınız, Shopify tarafından açılmadan önce %{app}
15
- gibi uygulamaların sizden çerezlere erişim izni istemesini zorunlu tutuyor.
16
- top_level_interaction_action: Devam
17
- request_storage_access_heading: "%{app} uygulamasının çerezlere erişmesi gerekiyor"
18
- request_storage_access_body: Böylece uygulama, kişisel bilgilerinizi geçici olarak
19
- saklayıp kimliğinizi doğrulayabilir. Devam et'e tıklayın ve çerezlerin uygulamayı
20
- kullanmasına izin verin.
21
- request_storage_access_footer: Çerezlerin süresi 30 gün sonra sonra sona erer.
22
- request_storage_access_action: Devam
@@ -3,20 +3,3 @@ vi:
3
3
  logged_out: Đã đăng xuất thành công
4
4
  could_not_log_in: Không thể đăng nhập vào cửa hàng trên Shopify
5
5
  invalid_shop_url: Miền cửa hàng không hợp lệ
6
- enable_cookies_heading: Bật cookie từ %{app}
7
- enable_cookies_body: Bạn phải bật cookie trong trình duyệt này theo cách thủ công
8
- để sử dụng %{app} trong Shopify.
9
- enable_cookies_footer: Cookie cho phép ứng dụng xác thực bạn bằng cách tạm thời
10
- lưu trữ tùy chọn và thông tin cá nhân của bạn. Những thông tin này sẽ hết hạn
11
- sau 30 ngày.
12
- enable_cookies_action: Bật cookie
13
- top_level_interaction_heading: Trình duyệt của bạn cần xác thực %{app}
14
- top_level_interaction_body: Trình duyệt của bạn cần các ứng dụng như %{app} để yêu
15
- cầu quyền truy cập vào cookie thì Shopify mới có thể mở giúp bạn.
16
- top_level_interaction_action: Tiếp tục
17
- request_storage_access_heading: "%{app} cần quyền truy cập cookie"
18
- request_storage_access_body: Nhờ vậy, ứng dụng có thể xác thực bạn bằng cách tạm
19
- thời lưu trữ thông tin cá nhân của bạn. Nhấp vào tiếp tục và cho phép cookie sử
20
- dụng ứng dụng.
21
- request_storage_access_footer: Cookie sẽ hết hạn sau 30 ngày.
22
- request_storage_access_action: Tiếp tục
@@ -3,14 +3,3 @@ zh-CN:
3
3
  logged_out: 已成功退出
4
4
  could_not_log_in: 无法登录到 Shopify 商店
5
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: 继续
@@ -3,14 +3,3 @@ zh-TW:
3
3
  logged_out: 登出成功
4
4
  could_not_log_in: 無法登入 Shopify 商店
5
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 可讓應用程式暫時儲存您的個人資訊,藉此驗證您的身分。按一下繼續並允許 Cookie 使用此應用程式。
15
- request_storage_access_footer: Cookie 將於 30 天後失效。
16
- request_storage_access_action: 繼續
data/config/routes.rb CHANGED
@@ -8,6 +8,7 @@ ShopifyApp::Engine.routes.draw do
8
8
  get login_url => :new, :as => :login
9
9
  post login_url => :create, :as => :authenticate
10
10
  get "logout" => :destroy, :as => :logout
11
+ get "patch_shopify_id_token" => :patch_shopify_id_token
11
12
 
12
13
  # Kept to prevent apps relying on these routes from breaking
13
14
  if login_url.gsub(%r{^/}, "") != "login"
@@ -26,6 +27,6 @@ ShopifyApp::Engine.routes.draw do
26
27
  end
27
28
 
28
29
  namespace :webhooks do
29
- post ":type" => :receive
30
+ post "(:type)" => :receive
30
31
  end
31
32
  end
data/docs/Quickstart.md CHANGED
@@ -34,8 +34,15 @@ HOST='https://some-random-words.trycloudflare.com/'
34
34
 
35
35
  ## Use Shopify App Bridge to embed your app in the Shopify Admin
36
36
 
37
- 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).
37
+ 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).
38
38
 
39
- 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).
39
+ If you are using the `shopify_app` gem **without** the [frontend react template](https://github.com/Shopify/shopify-frontend-template-react), 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. If your app is embedded and you want to display flash messages you will need to update the session storage to allow for 3rd party cookies. So that the flash messages can be save in the session cookie.
40
+
41
+ ```ruby
42
+ #session_store.rb
43
+ Rails.application.config.session_store(:cookie_store, key: '_example_session', expire_after: 14.days, secure: true, same_site: 'None')
44
+ ```
45
+
46
+ 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).
40
47
 
41
48
  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).
@@ -92,29 +92,6 @@ Edit `config/initializer/shopify_app.rb` and ensure the following configurations
92
92
  + config.shop_session_repository = 'Shop'
93
93
  ```
94
94
 
95
- #### Inspect server logs
96
-
97
- If you have checked the configurations above, and the app is still using cookies, then it is possible that the `shopify_app` gem defaulted to relying on cookies. This would happen when your browser allows third-party cookies and a session token was not successfully found as part of your request.
98
-
99
- In this case, check the server logs to see if the session token was invalid:
100
-
101
- ```los
102
- [ShopifyApp::JWT] Failed to validate JWT: [JWT::<Error>] <Failure message>
103
- ```
104
-
105
- *Example*
106
-
107
- ```
108
- [ShopifyApp::JWT] Failed to validate JWT: [JWT::ImmatureSignature] Signature nbf has not been reached
109
- ```
110
-
111
- **Note:** In a local development environment, you may want to temporarily update your `Gemfile` to point to a local instance of the `shopify_app` library instad of an installed gem. This will enable you to use a debugging tool like `byebug` to debug the library.
112
-
113
- ```diff
114
- - gem 'shopify_app', '~> 14.2'
115
- + gem 'shopify_app', path: '/path/to/shopify_app'
116
- ```
117
-
118
95
  ### My app can't make requests to the Shopify API
119
96
 
120
97
  > **Note:** Session tokens cannot be used to make authenticated requests to the Shopify API. Learn more about authenticating your backend requests to Shopify APIs at [Shopify API authentication](https://shopify.dev/concepts/about-apis/authentication).
data/docs/Upgrading.md CHANGED
@@ -8,6 +8,10 @@ This file documents important changes needed to upgrade your app's Shopify App v
8
8
 
9
9
  [Unreleased](#unreleased)
10
10
 
11
+ [Upgrading to `v22.2.0`](#upgrading-to-v2220)
12
+
13
+ [Upgrading to `v22.0.0`](#upgrading-to-v2200)
14
+
11
15
  [Upgrading to `v20.3.0`](#upgrading-to-v2030)
12
16
 
13
17
  [Upgrading to `v20.2.0`](#upgrading-to-v2020)
@@ -38,8 +42,67 @@ We also recommend the use of a staging site which matches your production enviro
38
42
 
39
43
  If you do run into issues, we recommend looking at our [debugging tips.](https://github.com/Shopify/shopify_app/blob/main/docs/Troubleshooting.md#debugging-tips)
40
44
 
45
+ ## Unreleased
46
+
47
+ #### (v23.0.0) - Deprecated methods in CallbackController
48
+ The following methods from `ShopifyApp::CallbackController` have been deprecated in `v23.0.0`
49
+ - `perform_after_authenticate_job`
50
+ - `install_webhooks`
51
+ - `perform_post_authenticate_jobs`
52
+
53
+ If you have overwritten these methods in your callback controller to modify the behavior of the inherited `CallbackController`, you will need to
54
+ update your app to use configurable option `config.custom_post_authenticate_tasks` instead. See [post authenticate tasks](/docs/shopify_app/authentication.md#post-authenticate-tasks)
55
+ for more information.
56
+
57
+ #### (v23.0.0) - Removed `ShopifyApp::JWTMiddleware`
58
+ The `ShopifyApp::JWTMiddleware` middleware has been removed in `v23.0.0`. This middleware was used to populate the following environment variables from the JWT session token:
59
+ - `request.env["jwt.token"]`
60
+ - `request.env["jwt.shopify_domain"]`
61
+ - `request.env["jwt.shopify_user_id"]`
62
+ - `request.env["jwt.expire_at"]`
63
+
64
+ If you are using any of these variables in your app, you'll need to replace them. You can instead include the `ShopifyApp::WithShopifyIdToken` concern, which does the same JWT parsing as the middleware, and exposes the same values in the following helper methods:
65
+ - `shopify_id_token`
66
+ - `jwt_shopify_domain`
67
+ - `jwt_shopify_user_id`
68
+ - `jwt_expire_at`
69
+
70
+ #### (v23.0.0) - Deprecated "ShopifyApp::JWT" class
71
+ The `ShopifyApp::JWT` class has been deprecated in `v23.0.0`. Use [ShopifyAPI::Auth::JwtPayload](https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/auth/jwt_payload.rb)
72
+ class from the `shopify_api` gem instead. A search and replace should be enough for this migration.
73
+ - `ShopifyAPI::Auth::JwtPayload` is a superset of the `ShopifyApp::JWT` class, and contains methods that were available in `ShopifyApp::JWT`.
74
+ - `ShopifyAPI::Auth::JwtPayload` raises `ShopifyAPI::Errors::InvalidJwtTokenError` if the token is invalid.
75
+
76
+ ## Upgrading to `v22.2.0`
77
+ #### Added new feature for zero redirect embedded app authorization flow - Token Exchange
78
+ A new embedded app authorization strategy has been introduced in `v22.2.0` that eliminates the redirects that were previously necessary for OAuth.
79
+ It can replace the existing installation and authorization code grant flow.
80
+ See [new embedded app authorization strategy](/README.md#new-embedded-app-authorization-strategy-token-exchange) for more information.
81
+
82
+ ## Upgrading to `v22.0.0`
83
+ #### Dropped support for Ruby 2.x
84
+ Support for Ruby 2.x has been dropped as it is no longer supported. You'll need to upgrade to 3.x.x
85
+
86
+ #### Renamed Controller Concerns
87
+ The following controller concerns have been renamed/replaced in `v21.10.0` and have now been removed. To upgrade, please rename any usage in your apps's controllers that include them to the following:
88
+
89
+ |Old Deprecated Controller Concern |Replaced By New Controller Concern|
90
+ |---|---|
91
+ |`Authenticated`|`EnsureHasSession`|
92
+ |`RequireKnownShop`|`EnsureInstalled`|
93
+
94
+ The new names better reflect what assurances the including the controller concern provide. The new concern provide similar if not identical functionality as the concerns they replaced.
95
+
96
+ #### Remove ScripttagManager
97
+ Script tag usage has largely been replaced with the adoption of [theme app extensions](https://shopify.dev/docs/apps/online-store/theme-app-extensions) and [thank you order status customization](https://shopify.dev/docs/apps/checkout/thank-you-order-status). The manager has been removed with this major release due to effective replacement and a goal to have parity in supported functionality across language stacks.
98
+
99
+ If you find yourself still using Scipt Tags and want to continue the pattern of declarative management of script tags this gem used to use, we recommend porting the logic [the manager used in prior versions](https://github.com/Shopify/shopify_app/blob/2336fabc6d0b45a4dee3f336455dace4d2d88bc4/lib/shopify_app/managers/scripttags_manager.rb#L4) and implementing it in a [post authentication job](https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/authentication.md#run-jobs-after-the-oauth-flow). This is the recommended flow to create script tags (or any other logic) for stores that install your app.
100
+
101
+ #### No longer rescue non-shopify API errors during customized OAuth flow
102
+ If you have customized authentication logic and are counting on the `CallbackController` to catch your error and redirect to login, you'll need to catch that error and redirect to `login_url_with_optional_shop`.
103
+
41
104
  ## Upgrading to 21.3.0
42
- The `Itp` controller concern has been removed from `LoginProtection` which is included by the `Authenticated` controller concern.
105
+ The `Itp` controller concern has been removed from `LoginProtection` which is included by the `Authenticated`/`EnsureHasSession` controller concern.
43
106
  If any of your controllers are dependant on methods from `Itp` then you can include `ShopifyApp::Itp` directly.
44
107
  You may notice a deprecation notice saying, `Itp will be removed in an upcoming version`.
45
108
  This is because we intend on removing `Itp` completely in `v22.0.0`, but this will work in the meantime.
@@ -1,41 +1,184 @@
1
1
  # Authentication
2
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.
3
+ The Shopify App gem implements [OAuth 2.0](https://shopify.dev/tutorials/authenticate-with-oauth) to get [session 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
4
 
5
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
6
 
7
- See [*Authenticate an embedded app using session tokens*](https://shopify.dev/tutorials/authenticate-your-app-using-session-tokens) to learn more.
7
+ See [*Getting started with session token authentication*](https://shopify.dev/docs/apps/auth/oauth/session-tokens/getting-started) to learn more.
8
8
 
9
9
  > ⚠️ Be sure you understand the differences between the types of authentication schemes before reading this guide.
10
10
 
11
11
  #### Table of contents
12
12
 
13
- [OAuth callback](#oauth-callback)
13
+ * [Supported types of OAuth Flow](#supported-types-of-oauth)
14
+ * [Token Exchange](#token-exchange)
15
+ * [Authorization Code Grant Flow](#authorization-code-grant-flow)
16
+ * [OAuth callback](#oauth-callback)
17
+ * [Customizing callback controller](#customizing-callback-controller)
18
+ * [Detecting scope changes](#detecting-scope-changes-1)
19
+ * [Run jobs after the OAuth flow](#post-authenticate-tasks)
20
+ * [Rotate API credentials](#rotate-api-credentials)
21
+ * [Making authenticated API requests after authorization](#making-authenticated-api-requests-after-authorization)
14
22
 
15
- [Run jobs after the OAuth flow](#run-jobs-after-the-oauth-flow)
23
+ ## Supported types of OAuth
24
+ > [!TIP]
25
+ > If you are building an embedded app, we **strongly** recommend using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
26
+ with [token exchange](#token-exchange) instead of the authorization code grant flow.
16
27
 
17
- [Rotate API credentials](#rotate-api-credentials)
28
+ 1. [Token Exchange](#token-exchange)
29
+ - Recommended and is only available for embedded apps
30
+ - Doesn't require redirects, which makes authorization faster and prevents flickering when loading the app
31
+ - Access scope changes are handled by Shopify when you use [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
32
+ 2. [Authorization Code Grant Flow](#authorization-code-grant-flow)
33
+ - Suitable for non-embedded apps
34
+ - Installations, and access scope changes are managed by the app
18
35
 
19
- [Available authentication mixins](#available-authentication-mixins)
20
- * [`ShopifyApp::Authenticated`](#shopifyappauthenticated)
21
- * [`ShopifyApp::EnsureAuthenticatedLinks`](#shopifyappensureauthenticatedlinks)
36
+ ## Token Exchange
22
37
 
23
- ## OAuth callback
38
+ OAuth process by exchanging the current user's [session token (shopify id token)](https://shopify.dev/docs/apps/auth/session-tokens) for an
39
+ [access token](https://shopify.dev/docs/apps/auth/access-token-types/online.md) to make
40
+ authenticated Shopify API queries. This will replace authorization code grant flow completely when your app is configured with [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).
24
41
 
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.
42
+ To enable token exchange authorization strategy, you can follow the steps in ["New embedded app authorization strategy"](/README.md#new-embedded-app-authorization-strategy).
43
+ Upon completion of the token exchange to get the access token, [post authenticated tasks](#post-authenticate-tasks) will be run.
26
44
 
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
45
+ Learn more about:
46
+ - [How token exchange works](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange)
47
+ - [Using Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
48
+ - [Configuring access scopes through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration)
33
49
 
34
- ## Run jobs after the OAuth flow
50
+ #### Handling invalid access tokens
51
+ If the access token used to make an API call is invalid, the token exchange strategy will handle the error and try to retrieve a new access token before retrying
52
+ the same operation.
53
+ See ["Re-fetching an access token when API returns Unauthorized"](/docs/shopify_app/sessions.md#re-fetching-an-access-token-when-api-returns-unauthorized) section for more information.
54
+
55
+ #### Detecting scope changes
56
+
57
+ ##### Shopify managed installation
58
+ If your access scopes are [configured through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration), scope changes will be handled by Shopify automatically.
59
+ Learn more about [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).
60
+ Using token exchange will ensure that the access token retrieved will always have the latest access scopes granted by the user.
61
+
62
+ ## Authorization Code Grant Flow
63
+ Authorization code grant flow is the OAuth flow that requires the app to redirect the user
64
+ to Shopify for installation/authorization of the app to access the shop's data. It is still required for apps that are not embedded.
65
+
66
+ If your app is not using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) with declared scopes in your `.toml` file, you can change the requested access scopes during OAuth flow
67
+ by adding the `scope` to your configurations - `ShopifyApp.configuration` & `ShopifyAPI::Context.setup`.
68
+
69
+
70
+ ```ruby
71
+ # config/initializers/shopify_app.rb
72
+
73
+ ShopifyApp.configure do |config|
74
+ ...
75
+ config.scope = ["read_discounts", "write_products"]
76
+ ...
77
+ end
78
+
79
+ ShopifyAPI::Context.setup(
80
+ ...
81
+ scope: ShopifyApp.configuration.scope,
82
+ ...
83
+ )
84
+ ```
85
+
86
+ To perform [authorization code grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant), you app will need to handle
87
+ [begin OAuth](#begin-oauth) and [OAuth callback](#oauth-callback) routes.
88
+
89
+ ### Begin OAuth
90
+ ShopifyApp automatically redirects the user to Shopify to complete OAuth to install the app when the `ShopifyApp.configuration.login_url` is reached.
91
+ Behind the scenes the ShopifyApp gem starts the process by calling `ShopifyAPI::Auth::Oauth.begin_auth` to build the
92
+ redirect URL with necessary parameters like the OAuth callback URL, scopes requested, type of access token (offline or online) requested, etc.
93
+ The ShopifyApp gem then redirect the merchant to Shopify, to ask for permission to install the app. (See [ShopifyApp::SessionsController.redirect_to_begin_oauth](https://github.com/Shopify/shopify_app/blob/main/app/controllers/shopify_app/sessions_controller.rb#L76-L96)
94
+ for detailed implementation)
95
+
96
+ ### OAuth callback
97
+
98
+ Shopify will redirect the merchant back to your app's callback URL once they approve the app installation.
99
+ Upon completing the OAuth flow, Shopify calls the app at `ShopifyApp.configuration.login_callback_url`. (This was provided to Shopify in the OAuth begin URL parameters)
100
+
101
+ The default callback controller [`ShopifyApp::CallbackController`](../../app/controllers/shopify_app/callback_controller.rb) provides the following behaviour:
102
+
103
+ 1. Logging into the shop and resetting the session
104
+ 2. Storing the session to the `SessionRepository`
105
+ 3. [Post authenticate tasks](#post-authenticate-tasks)
106
+ 4. Redirecting to the return address
107
+
108
+ #### Customizing callback controller
109
+ If you need to define a custom callback controller to handle your app's use case, you can configure the callback route to your controller.
110
+
111
+ Example:
112
+
113
+ 1. Create the new custom callback controller
114
+ ```ruby
115
+ # web/app/controllers/my_custom_callback_controller.rb
116
+
117
+ class MyCustomCallbackController
118
+ def callback
119
+ # My custom callback logic
120
+ end
121
+ end
122
+ ```
123
+
124
+ 2. Override callback routing to this controller
125
+
126
+ ```ruby
127
+ # web/config/routes.rb
128
+
129
+ Rails.application.routes.draw do
130
+ root to: "home#index"
131
+
132
+ # Overriding the callback controller to the new custom one.
133
+ # This must be added before mounting the ShopifyApp::Engine
134
+ get ShopifyApp.configuration.login_callback_url, to: 'my_custom_callback#callback'
135
+
136
+ mount ShopifyApp::Engine, at: "/api"
137
+
138
+ # other routes
139
+ end
140
+ ```
141
+
142
+ ### Detecting scope changes
143
+ When the OAuth process is completed, the created session has a `scope` field which holds all of the access scopes that were requested from the merchant at the time.
144
+
145
+ When an app's access scopes change, it needs to request merchants to go through OAuth again to renew its permissions.
146
+
147
+ See [Handling changes in access scopes](/docs/shopify_app/handling-access-scopes-changes.md).
148
+
149
+ ## Post Authenticate tasks
150
+ After authentication is complete, a few tasks are run by default by PostAuthenticateTasks:
151
+ 1. [Installing Webhooks](/docs/shopify_app/webhooks.md)
152
+ 2. [Run configured after_authenticate_job](#after_authenticate_job)
153
+
154
+ The [PostAuthenticateTasks](https://github.com/Shopify/shopify_app/blob/main/lib/shopify_app/auth/post_authenticate_tasks.rb)
155
+ class is responsible for triggering the webhooks manager for webhooks registration, and enqueue jobs from [after_authenticate_job](#after_authenticate_job).
156
+
157
+ If you simply need to enqueue more jobs to run after authenticate, use [after_authenticate_job](#after_authenticate_job) to define these jobs.
158
+
159
+ If your post authentication tasks is more complex and is different than just installing webhooks and enqueuing jobs,
160
+ you can customize the post authenticate tasks by creating a new class that has a `self.perform(session)` method,
161
+ and configuring `custom_post_authenticate_tasks` in the initializer.
162
+
163
+ ```ruby
164
+ # my_custom_post_authenticate_task.rb
165
+ class MyCustomPostAuthenticateTask
166
+ def self.perform(session)
167
+ # This will be triggered after OAuth callback and token exchange completion
168
+ end
169
+ end
170
+
171
+ # config/initializers/shopify_app.rb
172
+ ShopifyApp.configure do |config|
173
+ config.custom_post_authenticate_tasks = "MyCustomPostAuthenticateTask"
174
+ end
175
+ ```
176
+
177
+ #### after_authenticate_job
35
178
 
36
179
  See [`ShopifyApp::AfterAuthenticateJob`](/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb).
37
180
 
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:
181
+ 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. To configure the after authenticate job, update your initializer as follows:
39
182
 
40
183
  ```ruby
41
184
  ShopifyApp.configure do |config|
@@ -49,7 +192,7 @@ If you need the job to run synchronously add the `inline` flag:
49
192
 
50
193
  ```ruby
51
194
  ShopifyApp.configure do |config|
52
- config.after_authenticate_job = { job: Shopify::AfterAuthenticateJob, inline: true }
195
+ config.after_authenticate_job = { job: "Shopify::AfterAuthenticateJob", inline: true }
53
196
  end
54
197
  ```
55
198
 
@@ -63,7 +206,7 @@ If you want to perform that action only once, e.g. send a welcome email to the u
63
206
 
64
207
  ## Rotate API credentials
65
208
 
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).
209
+ 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
210
 
68
211
  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
212
 
@@ -72,6 +215,17 @@ config.secret = Rails.application.secrets.shopify_secret
72
215
  config.old_secret = Rails.application.secrets.old_shopify_secret
73
216
  ```
74
217
 
218
+ Also make sure the old secret is specified when setting up `ShopifyAPI::Context` as well:
219
+
220
+ ```ruby
221
+ ShopifyAPI::Context.setup(
222
+ api_key: ShopifyApp.configuration.api_key,
223
+ api_secret_key: ShopifyApp.configuration.secret,
224
+ # ...
225
+ old_api_secret_key: ShopifyApp.configuration.old_secret,
226
+ )
227
+ ```
228
+
75
229
  We've provided a generator which creates the job and an example rake task:
76
230
 
77
231
  ```sh
@@ -86,43 +240,10 @@ strategy.options[:old_client_secret] = ShopifyApp.configuration.old_secret
86
240
 
87
241
  > **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
242
 
89
- ## Available authentication mixins
243
+ ## Making authenticated API requests after authorization
244
+ After the app is installed onto a shop and has been granted all necessary permission, a new session record will be added to `SessionRepository#shop_storage`, or `SessionRepository#user_storage` if online sessions are enabled.
90
245
 
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
- #### Embedded apps and `ShopifyApp::Authenticated`
110
-
111
- Embedded Shopify Admin apps can only use the `ShopifyApp::Authenticated` controller concern *if* the requests originate from App Bridge's `authenticatedFetch` method. Those who include this concern in the `HomeController` or some other embedded controller will see what looks like an OAuth redirect loop as the `ShopifyApp::Authenticated` concern will be fighting with the App Bridge. For more details on how to handle embedded sessions, refer to [the session token documentation](https://shopify.dev/apps/auth/oauth/session-tokens).
112
-
113
- ### `ShopifyApp::EnsureAuthenticatedLinks`
114
-
115
- The [`ShopifyApp::EnsureAuthenticatedLinks`](/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb) concern helps authenticate users that access protected pages of your app directly.
116
-
117
- 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.
118
-
119
- *Example:*
120
-
121
- ```rb
122
- class AuthenticatedController < ApplicationController
123
- include ShopifyApp::EnsureAuthenticatedLinks
124
- include ShopifyApp::Authenticated
125
- end
126
- ```
246
+ When your app needs to make API requests to Shopify, `ShopifyApp`'s `ActiveSupport` controller concerns can help you retrieve the active session token from the repository to make the authenticate API call.
127
247
 
128
- 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.
248
+ - ⚠️ See [Sessions](./sessions.md) page to understand how sessions work.
249
+ - ⚠️ See [Controller Concerns](./controller-concerns.md) page to understand when to use which concern.