shopify-app-js 0.0.1-security → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shopify-app-js might be problematic. Click here for more details.

Files changed (424) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.changeset/flat-clouds-camp.md +5 -0
  4. package/.eslintrc.js +36 -0
  5. package/.github/CODEOWNERS +1 -0
  6. package/.github/ISSUE_TEMPLATE/BUG_REPORT.md +36 -0
  7. package/.github/ISSUE_TEMPLATE/ENHANCEMENT.md +9 -0
  8. package/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +9 -0
  9. package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
  10. package/.github/dependabot.yml +9 -0
  11. package/.github/workflows/changelog.yml +30 -0
  12. package/.github/workflows/ci.yml +22 -0
  13. package/.github/workflows/cla.yml +22 -0
  14. package/.github/workflows/close-waiting-for-response-issues.yml +20 -0
  15. package/.github/workflows/main-release.yml +36 -0
  16. package/.github/workflows/markdown_link_check.yml +14 -0
  17. package/.github/workflows/markdown_link_checker_config.json +9 -0
  18. package/.github/workflows/publish-experimental-build.yml +30 -0
  19. package/.github/workflows/release-candidate.yml +35 -0
  20. package/.github/workflows/remove-labels-on-activity.yml +16 -0
  21. package/.github/workflows/stale.yml +26 -0
  22. package/.prettierignore +5 -0
  23. package/.prettierrc +1 -0
  24. package/CODE_OF_CONDUCT.md +46 -0
  25. package/CONTRIBUTING.md +39 -0
  26. package/LICENSE.md +9 -0
  27. package/README.md +62 -3
  28. package/RELEASING.md +142 -0
  29. package/loom.config.ts +115 -0
  30. package/package.json +44 -3
  31. package/packages/.prettierrc +1 -0
  32. package/packages/shopify-app-express/CHANGELOG.md +316 -0
  33. package/packages/shopify-app-express/LICENSE.md +9 -0
  34. package/packages/shopify-app-express/README.md +93 -0
  35. package/packages/shopify-app-express/docs/reference/README.md +3 -0
  36. package/packages/shopify-app-express/docs/reference/auth.md +76 -0
  37. package/packages/shopify-app-express/docs/reference/cspHeaders.md +24 -0
  38. package/packages/shopify-app-express/docs/reference/ensureInstalledOnShop.md +17 -0
  39. package/packages/shopify-app-express/docs/reference/migrating-app-v6-api-lib-to-express-lib.md +345 -0
  40. package/packages/shopify-app-express/docs/reference/processWebhooks.md +67 -0
  41. package/packages/shopify-app-express/docs/reference/redirectOutOfApp.md +77 -0
  42. package/packages/shopify-app-express/docs/reference/redirectToShopifyOrAppRoot.md +20 -0
  43. package/packages/shopify-app-express/docs/reference/shopifyApp.md +148 -0
  44. package/packages/shopify-app-express/docs/reference/validateAuthenticatedSession.md +30 -0
  45. package/packages/shopify-app-express/loom.config.ts +27 -0
  46. package/packages/shopify-app-express/package.json +54 -0
  47. package/packages/shopify-app-express/src/__tests__/index.test.ts +100 -0
  48. package/packages/shopify-app-express/src/__tests__/integration/oauth.test.ts +445 -0
  49. package/packages/shopify-app-express/src/__tests__/integration/responses.ts +135 -0
  50. package/packages/shopify-app-express/src/__tests__/integration/types.ts +22 -0
  51. package/packages/shopify-app-express/src/__tests__/integration/utils.ts +68 -0
  52. package/packages/shopify-app-express/src/__tests__/integration/webhooks.test.ts +208 -0
  53. package/packages/shopify-app-express/src/__tests__/redirect-to-auth.test.ts +103 -0
  54. package/packages/shopify-app-express/src/__tests__/setup-jest.ts +8 -0
  55. package/packages/shopify-app-express/src/__tests__/test-helper.ts +204 -0
  56. package/packages/shopify-app-express/src/app-installations.ts +42 -0
  57. package/packages/shopify-app-express/src/auth/__tests__/auth.test.ts +303 -0
  58. package/packages/shopify-app-express/src/auth/auth-callback.ts +131 -0
  59. package/packages/shopify-app-express/src/auth/index.ts +32 -0
  60. package/packages/shopify-app-express/src/auth/types.ts +13 -0
  61. package/packages/shopify-app-express/src/config-types.ts +38 -0
  62. package/packages/shopify-app-express/src/error.ts +8 -0
  63. package/packages/shopify-app-express/src/index.ts +171 -0
  64. package/packages/shopify-app-express/src/middlewares/__tests__/csp-headers.test.ts +69 -0
  65. package/packages/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts +193 -0
  66. package/packages/shopify-app-express/src/middlewares/__tests__/redirect-to-shopify-or-app-root.test.ts +59 -0
  67. package/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts +319 -0
  68. package/packages/shopify-app-express/src/middlewares/csp-headers.ts +31 -0
  69. package/packages/shopify-app-express/src/middlewares/ensure-installed-on-shop.ts +176 -0
  70. package/packages/shopify-app-express/src/middlewares/has-valid-access-token.ts +25 -0
  71. package/packages/shopify-app-express/src/middlewares/index.ts +15 -0
  72. package/packages/shopify-app-express/src/middlewares/redirect-to-shopify-or-app-root.ts +39 -0
  73. package/packages/shopify-app-express/src/middlewares/types.ts +6 -0
  74. package/packages/shopify-app-express/src/middlewares/validate-authenticated-session.ts +142 -0
  75. package/packages/shopify-app-express/src/redirect-out-of-app.ts +80 -0
  76. package/packages/shopify-app-express/src/redirect-to-auth.ts +70 -0
  77. package/packages/shopify-app-express/src/types.ts +26 -0
  78. package/packages/shopify-app-express/src/version.ts +1 -0
  79. package/packages/shopify-app-express/src/webhooks/__tests__/process.test.ts +138 -0
  80. package/packages/shopify-app-express/src/webhooks/index.ts +54 -0
  81. package/packages/shopify-app-express/src/webhooks/process.ts +22 -0
  82. package/packages/shopify-app-express/src/webhooks/types.ts +24 -0
  83. package/packages/shopify-app-express/tsconfig.json +10 -0
  84. package/packages/shopify-app-remix/.eslintrc.js +3 -0
  85. package/packages/shopify-app-remix/CHANGELOG.md +569 -0
  86. package/packages/shopify-app-remix/LICENSE.md +9 -0
  87. package/packages/shopify-app-remix/README.md +223 -0
  88. package/packages/shopify-app-remix/docs/build-docs.sh +11 -0
  89. package/packages/shopify-app-remix/docs/generated/generated_docs_data.json +18650 -0
  90. package/packages/shopify-app-remix/docs/generated/generated_static_pages.json +540 -0
  91. package/packages/shopify-app-remix/docs/staticPages/admin.doc.ts +130 -0
  92. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/auth-cors.example.tsx +11 -0
  93. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/auth.example.tsx +19 -0
  94. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/graphql.example.tsx +29 -0
  95. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/headers.example.tsx +10 -0
  96. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/rest-resources.example.tsx +9 -0
  97. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/rest.example.tsx +17 -0
  98. package/packages/shopify-app-remix/docs/staticPages/examples/guides/future-flags/config.example.ts +8 -0
  99. package/packages/shopify-app-remix/docs/staticPages/examples/guides/future-flags/unstable.example.ts +9 -0
  100. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/.graphqlrc.ts +16 -0
  101. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.npm.example.sh +2 -0
  102. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.pnpm.example.sh +2 -0
  103. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.yarn.example.sh +2 -0
  104. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/package.json +5 -0
  105. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.npm.example.sh +1 -0
  106. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.pnpm.example.sh +1 -0
  107. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.yarn.example.sh +1 -0
  108. package/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/config.example.ts +19 -0
  109. package/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/endpoint.example.ts +24 -0
  110. package/packages/shopify-app-remix/docs/staticPages/examples/index/app-provider.example.ts +30 -0
  111. package/packages/shopify-app-remix/docs/staticPages/examples/index/boundaries.example.ts +9 -0
  112. package/packages/shopify-app-remix/docs/staticPages/examples/index/create.npm.example.sh +1 -0
  113. package/packages/shopify-app-remix/docs/staticPages/examples/index/create.pnpm.example.sh +1 -0
  114. package/packages/shopify-app-remix/docs/staticPages/examples/index/create.yarn.example.sh +1 -0
  115. package/packages/shopify-app-remix/docs/staticPages/examples/index/embedded-app-auth-strategy-config.example.ts +10 -0
  116. package/packages/shopify-app-remix/docs/staticPages/examples/index/entry-server.example.ts +12 -0
  117. package/packages/shopify-app-remix/docs/staticPages/examples/index/install.npm.example.sh +1 -0
  118. package/packages/shopify-app-remix/docs/staticPages/examples/index/install.pnpm.example.sh +1 -0
  119. package/packages/shopify-app-remix/docs/staticPages/examples/index/install.yarn.example.sh +1 -0
  120. package/packages/shopify-app-remix/docs/staticPages/examples/index/shopify-app.example.ts +14 -0
  121. package/packages/shopify-app-remix/docs/staticPages/examples/index/splat-route.example.ts +11 -0
  122. package/packages/shopify-app-remix/docs/staticPages/future-flags.doc.ts +94 -0
  123. package/packages/shopify-app-remix/docs/staticPages/graphql-types.doc.ts +148 -0
  124. package/packages/shopify-app-remix/docs/staticPages/index.doc.ts +227 -0
  125. package/packages/shopify-app-remix/docs/staticPages/webhooks.doc.ts +64 -0
  126. package/packages/shopify-app-remix/docs/tsconfig.docs.json +9 -0
  127. package/packages/shopify-app-remix/docs/typeOverride.json +1 -0
  128. package/packages/shopify-app-remix/docs/upcoming_changes.md +153 -0
  129. package/packages/shopify-app-remix/loom.config.ts +57 -0
  130. package/packages/shopify-app-remix/package.json +93 -0
  131. package/packages/shopify-app-remix/src/react/.eslintrc.js +16 -0
  132. package/packages/shopify-app-remix/src/react/__tests__/test-helper.ts +22 -0
  133. package/packages/shopify-app-remix/src/react/components/AppProvider/AppProvider.doc.ts +34 -0
  134. package/packages/shopify-app-remix/src/react/components/AppProvider/AppProvider.tsx +121 -0
  135. package/packages/shopify-app-remix/src/react/components/AppProvider/__tests__/AppProvider.test.tsx +68 -0
  136. package/packages/shopify-app-remix/src/react/components/AppProvider/index.ts +1 -0
  137. package/packages/shopify-app-remix/src/react/components/RemixPolarisLink.tsx +14 -0
  138. package/packages/shopify-app-remix/src/react/components/index.ts +1 -0
  139. package/packages/shopify-app-remix/src/react/const.ts +2 -0
  140. package/packages/shopify-app-remix/src/react/index.ts +1 -0
  141. package/packages/shopify-app-remix/src/server/.eslintrc.js +16 -0
  142. package/packages/shopify-app-remix/src/server/__test-helpers/__tests__/request-mock.test.ts +171 -0
  143. package/packages/shopify-app-remix/src/server/__test-helpers/const.ts +11 -0
  144. package/packages/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts +119 -0
  145. package/packages/shopify-app-remix/src/server/__test-helpers/expect-begin-auth-redirect.ts +22 -0
  146. package/packages/shopify-app-remix/src/server/__test-helpers/expect-document-request-headers.ts +25 -0
  147. package/packages/shopify-app-remix/src/server/__test-helpers/expect-exit-iframe.ts +37 -0
  148. package/packages/shopify-app-remix/src/server/__test-helpers/expect-login-redirect.ts +8 -0
  149. package/packages/shopify-app-remix/src/server/__test-helpers/expect-storefront-api-client.ts +50 -0
  150. package/packages/shopify-app-remix/src/server/__test-helpers/get-hmac.ts +10 -0
  151. package/packages/shopify-app-remix/src/server/__test-helpers/get-jwt.ts +31 -0
  152. package/packages/shopify-app-remix/src/server/__test-helpers/get-thrown-response.ts +17 -0
  153. package/packages/shopify-app-remix/src/server/__test-helpers/index.ts +14 -0
  154. package/packages/shopify-app-remix/src/server/__test-helpers/request-mock.ts +169 -0
  155. package/packages/shopify-app-remix/src/server/__test-helpers/setup-valid-session.ts +45 -0
  156. package/packages/shopify-app-remix/src/server/__test-helpers/sign-request-cookie.ts +21 -0
  157. package/packages/shopify-app-remix/src/server/__test-helpers/test-config.ts +112 -0
  158. package/packages/shopify-app-remix/src/server/__tests__/override-logger.test.ts +101 -0
  159. package/packages/shopify-app-remix/src/server/__tests__/shopify-app.test.ts +119 -0
  160. package/packages/shopify-app-remix/src/server/adapters/__tests__/node-app-bridge-url.test.ts +18 -0
  161. package/packages/shopify-app-remix/src/server/adapters/__tests__/node.test.ts +28 -0
  162. package/packages/shopify-app-remix/src/server/adapters/node/__tests__/setup-jest.ts +10 -0
  163. package/packages/shopify-app-remix/src/server/adapters/node/index.ts +20 -0
  164. package/packages/shopify-app-remix/src/server/adapters/vercel/__tests__/setup-jest.ts +12 -0
  165. package/packages/shopify-app-remix/src/server/adapters/vercel/index.ts +13 -0
  166. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts +181 -0
  167. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/doc-request-path.test.ts +151 -0
  168. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/exit-i-frame-path.test.ts +99 -0
  169. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/patch-session-token-path.test.ts +58 -0
  170. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/reject-bot.test.ts +23 -0
  171. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/respond-to-options.test.ts +50 -0
  172. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts +91 -0
  173. package/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.admin.doc.ts +37 -0
  174. package/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.ts +201 -0
  175. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/cancel.test.ts +258 -0
  176. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/check.test.ts +254 -0
  177. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/mock-responses.ts +65 -0
  178. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/request.test.ts +373 -0
  179. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/require.test.ts +316 -0
  180. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/authenticate.admin.billing.doc.ts +28 -0
  181. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/cancel.ts +37 -0
  182. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/check.ts +38 -0
  183. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/index.ts +4 -0
  184. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/request.ts +101 -0
  185. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/require.ts +56 -0
  186. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/types.ts +383 -0
  187. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/__tests__/redirect.test.ts +313 -0
  188. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/__tests__/validate-redirect-url.test.ts +84 -0
  189. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/begin-auth.ts +17 -0
  190. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts +22 -0
  191. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.ts +18 -0
  192. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.ts +25 -0
  193. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/handle-client-error.ts +43 -0
  194. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/index.ts +14 -0
  195. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-auth-page.ts +28 -0
  196. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts +23 -0
  197. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.ts +21 -0
  198. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-with-app-bridge-headers.ts +13 -0
  199. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-with-exitiframe.ts +28 -0
  200. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect.ts +83 -0
  201. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/render-app-bridge.ts +46 -0
  202. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +28 -0
  203. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/validate-redirect-url.ts +67 -0
  204. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/validate-shop-and-host-params.ts +28 -0
  205. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts +222 -0
  206. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-callback-path.test.ts +395 -0
  207. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-path.test.ts +83 -0
  208. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts +183 -0
  209. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/ensure-installed-on-shop.test.ts +234 -0
  210. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/session-token-header-path.test.ts +79 -0
  211. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts +189 -0
  212. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts +310 -0
  213. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts +333 -0
  214. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/token-exchange.ts +169 -0
  215. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/types.ts +29 -0
  216. package/packages/shopify-app-remix/src/server/authenticate/admin/types.ts +199 -0
  217. package/packages/shopify-app-remix/src/server/authenticate/const.ts +15 -0
  218. package/packages/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts +142 -0
  219. package/packages/shopify-app-remix/src/server/authenticate/flow/authenticate.flow.doc.ts +34 -0
  220. package/packages/shopify-app-remix/src/server/authenticate/flow/authenticate.ts +71 -0
  221. package/packages/shopify-app-remix/src/server/authenticate/flow/types.ts +90 -0
  222. package/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/add-response-headers.test.ts +25 -0
  223. package/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/app-bridge-url.test.ts +21 -0
  224. package/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/idempotent-promise-handler.test.ts +104 -0
  225. package/packages/shopify-app-remix/src/server/authenticate/helpers/add-response-headers.ts +43 -0
  226. package/packages/shopify-app-remix/src/server/authenticate/helpers/app-bridge-url.ts +10 -0
  227. package/packages/shopify-app-remix/src/server/authenticate/helpers/ensure-cors-headers.ts +38 -0
  228. package/packages/shopify-app-remix/src/server/authenticate/helpers/get-session-token-header.ts +11 -0
  229. package/packages/shopify-app-remix/src/server/authenticate/helpers/idempotent-promise-handler.ts +45 -0
  230. package/packages/shopify-app-remix/src/server/authenticate/helpers/index.ts +8 -0
  231. package/packages/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts +13 -0
  232. package/packages/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts +29 -0
  233. package/packages/shopify-app-remix/src/server/authenticate/helpers/respond-to-options-request.ts +26 -0
  234. package/packages/shopify-app-remix/src/server/authenticate/helpers/validate-session-token.ts +32 -0
  235. package/packages/shopify-app-remix/src/server/authenticate/login/__tests__/login.test.ts +157 -0
  236. package/packages/shopify-app-remix/src/server/authenticate/login/login.ts +53 -0
  237. package/packages/shopify-app-remix/src/server/authenticate/public/__tests__/factory.test.ts +224 -0
  238. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts +282 -0
  239. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts +36 -0
  240. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts +90 -0
  241. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts +164 -0
  242. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/__tests__/authenticate.test.ts +142 -0
  243. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.ts +23 -0
  244. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/authenticate.ts +45 -0
  245. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/types.ts +65 -0
  246. package/packages/shopify-app-remix/src/server/authenticate/public/factory.ts +49 -0
  247. package/packages/shopify-app-remix/src/server/authenticate/public/index.ts +1 -0
  248. package/packages/shopify-app-remix/src/server/authenticate/public/types.ts +77 -0
  249. package/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts +207 -0
  250. package/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/mock-responses.ts +45 -0
  251. package/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts +210 -0
  252. package/packages/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts +113 -0
  253. package/packages/shopify-app-remix/src/server/authenticate/webhooks/authenticate.webhooks.doc.ts +28 -0
  254. package/packages/shopify-app-remix/src/server/authenticate/webhooks/index.ts +1 -0
  255. package/packages/shopify-app-remix/src/server/authenticate/webhooks/register.ts +48 -0
  256. package/packages/shopify-app-remix/src/server/authenticate/webhooks/types.ts +246 -0
  257. package/packages/shopify-app-remix/src/server/boundary/__tests__/error.test.tsx +36 -0
  258. package/packages/shopify-app-remix/src/server/boundary/__tests__/headers.test.ts +66 -0
  259. package/packages/shopify-app-remix/src/server/boundary/error.tsx +14 -0
  260. package/packages/shopify-app-remix/src/server/boundary/headers.ts +15 -0
  261. package/packages/shopify-app-remix/src/server/boundary/index.ts +39 -0
  262. package/packages/shopify-app-remix/src/server/boundary/types.ts +4 -0
  263. package/packages/shopify-app-remix/src/server/clients/admin/authenticate.admin.api.doc.ts +34 -0
  264. package/packages/shopify-app-remix/src/server/clients/admin/factory.ts +30 -0
  265. package/packages/shopify-app-remix/src/server/clients/admin/graphql.ts +39 -0
  266. package/packages/shopify-app-remix/src/server/clients/admin/index.ts +2 -0
  267. package/packages/shopify-app-remix/src/server/clients/admin/rest.ts +157 -0
  268. package/packages/shopify-app-remix/src/server/clients/admin/types.ts +178 -0
  269. package/packages/shopify-app-remix/src/server/clients/index.ts +2 -0
  270. package/packages/shopify-app-remix/src/server/clients/storefront/authenticate.storefront.api.doc.ts +34 -0
  271. package/packages/shopify-app-remix/src/server/clients/storefront/factory.ts +32 -0
  272. package/packages/shopify-app-remix/src/server/clients/storefront/index.ts +2 -0
  273. package/packages/shopify-app-remix/src/server/clients/storefront/types.ts +31 -0
  274. package/packages/shopify-app-remix/src/server/clients/types.ts +27 -0
  275. package/packages/shopify-app-remix/src/server/config-types.ts +293 -0
  276. package/packages/shopify-app-remix/src/server/errors.ts +3 -0
  277. package/packages/shopify-app-remix/src/server/future/flags.ts +57 -0
  278. package/packages/shopify-app-remix/src/server/index.ts +20 -0
  279. package/packages/shopify-app-remix/src/server/override-logger.ts +42 -0
  280. package/packages/shopify-app-remix/src/server/shopify-app.doc.ts +45 -0
  281. package/packages/shopify-app-remix/src/server/shopify-app.ts +198 -0
  282. package/packages/shopify-app-remix/src/server/types.ts +509 -0
  283. package/packages/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts +30 -0
  284. package/packages/shopify-app-remix/src/server/unauthenticated/admin/factory.ts +29 -0
  285. package/packages/shopify-app-remix/src/server/unauthenticated/admin/index.ts +1 -0
  286. package/packages/shopify-app-remix/src/server/unauthenticated/admin/types.ts +117 -0
  287. package/packages/shopify-app-remix/src/server/unauthenticated/admin/unauthenticated.admin.doc.ts +40 -0
  288. package/packages/shopify-app-remix/src/server/unauthenticated/helpers/get-offline-session.ts +13 -0
  289. package/packages/shopify-app-remix/src/server/unauthenticated/helpers/index.ts +1 -0
  290. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/__tests__/factory.test.ts +30 -0
  291. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/factory.ts +28 -0
  292. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/index.ts +1 -0
  293. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/types.ts +39 -0
  294. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/unauthenticated.storefront.doc.ts +40 -0
  295. package/packages/shopify-app-remix/src/server/unauthenticated/types.ts +68 -0
  296. package/packages/shopify-app-remix/src/server/version.ts +1 -0
  297. package/packages/shopify-app-remix/tsconfig.json +11 -0
  298. package/packages/shopify-app-session-storage/CHANGELOG.md +149 -0
  299. package/packages/shopify-app-session-storage/LICENSE.md +9 -0
  300. package/packages/shopify-app-session-storage/README.md +5 -0
  301. package/packages/shopify-app-session-storage/implementing-session-storage.md +214 -0
  302. package/packages/shopify-app-session-storage/loom.config.ts +45 -0
  303. package/packages/shopify-app-session-storage/package.json +44 -0
  304. package/packages/shopify-app-session-storage/src/__tests__/setup-jest.ts +8 -0
  305. package/packages/shopify-app-session-storage/src/abstract-migration-engine.ts +53 -0
  306. package/packages/shopify-app-session-storage/src/index.ts +3 -0
  307. package/packages/shopify-app-session-storage/src/rdbms-session-storage-migrator.ts +59 -0
  308. package/packages/shopify-app-session-storage/src/types.ts +174 -0
  309. package/packages/shopify-app-session-storage/tsconfig.json +10 -0
  310. package/packages/shopify-app-session-storage-dynamodb/CHANGELOG.md +150 -0
  311. package/packages/shopify-app-session-storage-dynamodb/LICENSE.md +9 -0
  312. package/packages/shopify-app-session-storage-dynamodb/README.md +75 -0
  313. package/packages/shopify-app-session-storage-dynamodb/loom.config.ts +27 -0
  314. package/packages/shopify-app-session-storage-dynamodb/package.json +51 -0
  315. package/packages/shopify-app-session-storage-dynamodb/src/__tests__/dynamodb.test.ts +93 -0
  316. package/packages/shopify-app-session-storage-dynamodb/src/__tests__/setup-jest.ts +8 -0
  317. package/packages/shopify-app-session-storage-dynamodb/src/dynamodb.ts +126 -0
  318. package/packages/shopify-app-session-storage-dynamodb/tsconfig.json +10 -0
  319. package/packages/shopify-app-session-storage-kv/CHANGELOG.md +204 -0
  320. package/packages/shopify-app-session-storage-kv/LICENSE.md +9 -0
  321. package/packages/shopify-app-session-storage-kv/README.md +41 -0
  322. package/packages/shopify-app-session-storage-kv/loom.config.ts +27 -0
  323. package/packages/shopify-app-session-storage-kv/package.json +52 -0
  324. package/packages/shopify-app-session-storage-kv/src/__tests__/kv-namespace-dummy-worker.ts +8 -0
  325. package/packages/shopify-app-session-storage-kv/src/__tests__/kv.test.ts +22 -0
  326. package/packages/shopify-app-session-storage-kv/src/__tests__/setup-jest.ts +8 -0
  327. package/packages/shopify-app-session-storage-kv/src/kv.ts +87 -0
  328. package/packages/shopify-app-session-storage-kv/tsconfig.json +11 -0
  329. package/packages/shopify-app-session-storage-memory/CHANGELOG.md +190 -0
  330. package/packages/shopify-app-session-storage-memory/LICENSE.md +9 -0
  331. package/packages/shopify-app-session-storage-memory/README.md +19 -0
  332. package/packages/shopify-app-session-storage-memory/loom.config.ts +27 -0
  333. package/packages/shopify-app-session-storage-memory/package.json +46 -0
  334. package/packages/shopify-app-session-storage-memory/src/__tests__/memory.test.ts +12 -0
  335. package/packages/shopify-app-session-storage-memory/src/__tests__/setup-jest.ts +8 -0
  336. package/packages/shopify-app-session-storage-memory/src/memory.ts +34 -0
  337. package/packages/shopify-app-session-storage-memory/tsconfig.json +10 -0
  338. package/packages/shopify-app-session-storage-mongodb/CHANGELOG.md +208 -0
  339. package/packages/shopify-app-session-storage-mongodb/LICENSE.md +9 -0
  340. package/packages/shopify-app-session-storage-mongodb/README.md +30 -0
  341. package/packages/shopify-app-session-storage-mongodb/loom.config.ts +27 -0
  342. package/packages/shopify-app-session-storage-mongodb/package.json +49 -0
  343. package/packages/shopify-app-session-storage-mongodb/src/__tests__/mongodb.test.ts +54 -0
  344. package/packages/shopify-app-session-storage-mongodb/src/__tests__/setup-jest.ts +8 -0
  345. package/packages/shopify-app-session-storage-mongodb/src/mongodb.ts +120 -0
  346. package/packages/shopify-app-session-storage-mongodb/tsconfig.json +10 -0
  347. package/packages/shopify-app-session-storage-mysql/CHANGELOG.md +220 -0
  348. package/packages/shopify-app-session-storage-mysql/LICENSE.md +9 -0
  349. package/packages/shopify-app-session-storage-mysql/README.md +41 -0
  350. package/packages/shopify-app-session-storage-mysql/loom.config.ts +27 -0
  351. package/packages/shopify-app-session-storage-mysql/package.json +50 -0
  352. package/packages/shopify-app-session-storage-mysql/src/__tests__/mysql.test.ts +137 -0
  353. package/packages/shopify-app-session-storage-mysql/src/__tests__/setup-jest.ts +8 -0
  354. package/packages/shopify-app-session-storage-mysql/src/migrations.ts +18 -0
  355. package/packages/shopify-app-session-storage-mysql/src/mysql-connection.ts +108 -0
  356. package/packages/shopify-app-session-storage-mysql/src/mysql-migrator.ts +53 -0
  357. package/packages/shopify-app-session-storage-mysql/src/mysql.ts +178 -0
  358. package/packages/shopify-app-session-storage-mysql/tsconfig.json +10 -0
  359. package/packages/shopify-app-session-storage-postgresql/CHANGELOG.md +210 -0
  360. package/packages/shopify-app-session-storage-postgresql/LICENSE.md +9 -0
  361. package/packages/shopify-app-session-storage-postgresql/README.md +39 -0
  362. package/packages/shopify-app-session-storage-postgresql/loom.config.ts +27 -0
  363. package/packages/shopify-app-session-storage-postgresql/package.json +52 -0
  364. package/packages/shopify-app-session-storage-postgresql/src/__tests__/migrate-to-case-sensitivity.test.ts +344 -0
  365. package/packages/shopify-app-session-storage-postgresql/src/__tests__/postgresql.test.ts +136 -0
  366. package/packages/shopify-app-session-storage-postgresql/src/__tests__/setup-jest.ts +8 -0
  367. package/packages/shopify-app-session-storage-postgresql/src/migrations.ts +70 -0
  368. package/packages/shopify-app-session-storage-postgresql/src/postgres-connection.ts +94 -0
  369. package/packages/shopify-app-session-storage-postgresql/src/postgres-migrator.ts +27 -0
  370. package/packages/shopify-app-session-storage-postgresql/src/postgresql.ts +178 -0
  371. package/packages/shopify-app-session-storage-postgresql/tsconfig.json +10 -0
  372. package/packages/shopify-app-session-storage-prisma/CHANGELOG.md +148 -0
  373. package/packages/shopify-app-session-storage-prisma/LICENSE.md +9 -0
  374. package/packages/shopify-app-session-storage-prisma/README.md +68 -0
  375. package/packages/shopify-app-session-storage-prisma/loom.config.ts +27 -0
  376. package/packages/shopify-app-session-storage-prisma/package.json +51 -0
  377. package/packages/shopify-app-session-storage-prisma/prisma/migrations/20230425184828_init/migration.sql +11 -0
  378. package/packages/shopify-app-session-storage-prisma/prisma/migrations/20230906155758_mySession/migration.sql +11 -0
  379. package/packages/shopify-app-session-storage-prisma/prisma/migrations/migration_lock.toml +3 -0
  380. package/packages/shopify-app-session-storage-prisma/prisma/schema.prisma +30 -0
  381. package/packages/shopify-app-session-storage-prisma/src/__tests__/prisma.test.ts +66 -0
  382. package/packages/shopify-app-session-storage-prisma/src/__tests__/setup-jest.ts +8 -0
  383. package/packages/shopify-app-session-storage-prisma/src/prisma.ts +145 -0
  384. package/packages/shopify-app-session-storage-prisma/tsconfig.json +10 -0
  385. package/packages/shopify-app-session-storage-redis/CHANGELOG.md +200 -0
  386. package/packages/shopify-app-session-storage-redis/LICENSE.md +9 -0
  387. package/packages/shopify-app-session-storage-redis/README.md +38 -0
  388. package/packages/shopify-app-session-storage-redis/loom.config.ts +27 -0
  389. package/packages/shopify-app-session-storage-redis/package.json +50 -0
  390. package/packages/shopify-app-session-storage-redis/src/__tests__/migration-test-data.ts +46 -0
  391. package/packages/shopify-app-session-storage-redis/src/__tests__/redis.conf +2 -0
  392. package/packages/shopify-app-session-storage-redis/src/__tests__/redis.test.ts +236 -0
  393. package/packages/shopify-app-session-storage-redis/src/__tests__/setup-jest.ts +8 -0
  394. package/packages/shopify-app-session-storage-redis/src/migrations.ts +36 -0
  395. package/packages/shopify-app-session-storage-redis/src/redis-connection.ts +64 -0
  396. package/packages/shopify-app-session-storage-redis/src/redis-migrator.ts +58 -0
  397. package/packages/shopify-app-session-storage-redis/src/redis.ts +167 -0
  398. package/packages/shopify-app-session-storage-redis/tsconfig.json +10 -0
  399. package/packages/shopify-app-session-storage-sqlite/CHANGELOG.md +202 -0
  400. package/packages/shopify-app-session-storage-sqlite/LICENSE.md +9 -0
  401. package/packages/shopify-app-session-storage-sqlite/README.md +25 -0
  402. package/packages/shopify-app-session-storage-sqlite/loom.config.ts +27 -0
  403. package/packages/shopify-app-session-storage-sqlite/package.json +51 -0
  404. package/packages/shopify-app-session-storage-sqlite/src/__tests__/setup-jest.ts +8 -0
  405. package/packages/shopify-app-session-storage-sqlite/src/__tests__/sqlite.test.ts +44 -0
  406. package/packages/shopify-app-session-storage-sqlite/src/migrations.ts +54 -0
  407. package/packages/shopify-app-session-storage-sqlite/src/sqlite-connection.ts +80 -0
  408. package/packages/shopify-app-session-storage-sqlite/src/sqlite-migrator.ts +34 -0
  409. package/packages/shopify-app-session-storage-sqlite/src/sqlite.ts +147 -0
  410. package/packages/shopify-app-session-storage-sqlite/tsconfig.json +10 -0
  411. package/packages/shopify-app-session-storage-test-utils/CHANGELOG.md +185 -0
  412. package/packages/shopify-app-session-storage-test-utils/LICENSE.md +9 -0
  413. package/packages/shopify-app-session-storage-test-utils/loom.config.ts +27 -0
  414. package/packages/shopify-app-session-storage-test-utils/package.json +48 -0
  415. package/packages/shopify-app-session-storage-test-utils/src/__tests__/session-test-utils.test.ts +273 -0
  416. package/packages/shopify-app-session-storage-test-utils/src/battery-of-tests.ts +250 -0
  417. package/packages/shopify-app-session-storage-test-utils/src/index.ts +2 -0
  418. package/packages/shopify-app-session-storage-test-utils/src/session-test-utils.ts +24 -0
  419. package/packages/shopify-app-session-storage-test-utils/src/utils.ts +71 -0
  420. package/packages/shopify-app-session-storage-test-utils/tsconfig.json +10 -0
  421. package/tests/setup/build.js +1 -0
  422. package/tests/setup/setup-jest.ts +8 -0
  423. package/tsconfig.base.json +41 -0
  424. package/tsconfig.json +19 -0
@@ -0,0 +1,169 @@
1
+ import {
2
+ HttpResponseError,
3
+ InvalidJwtError,
4
+ RequestedTokenType,
5
+ Session,
6
+ Shopify,
7
+ ShopifyRestResources,
8
+ } from '@shopify/shopify-api';
9
+
10
+ import {AppConfig, AppConfigArg} from '../../../config-types';
11
+ import {BasicParams} from '../../../types';
12
+ import {respondToInvalidSessionToken} from '../../helpers';
13
+ import {handleClientErrorFactory, triggerAfterAuthHook} from '../helpers';
14
+ import {HandleAdminClientError} from '../../../clients';
15
+ import type {
16
+ ApiConfigWithFutureFlags,
17
+ ApiFutureFlags,
18
+ } from '../../../future/flags';
19
+
20
+ import {AuthorizationStrategy, SessionContext, OnErrorOptions} from './types';
21
+
22
+ export class TokenExchangeStrategy<Config extends AppConfigArg>
23
+ implements AuthorizationStrategy
24
+ {
25
+ protected api: Shopify<
26
+ ApiConfigWithFutureFlags<Config['future']>,
27
+ ShopifyRestResources,
28
+ ApiFutureFlags<Config['future']>
29
+ >;
30
+
31
+ protected config: AppConfig;
32
+ protected logger: Shopify['logger'];
33
+
34
+ public constructor({api, config, logger}: BasicParams<Config['future']>) {
35
+ this.api = api;
36
+ this.config = config;
37
+ this.logger = logger;
38
+ }
39
+
40
+ public async respondToOAuthRequests(_request: Request): Promise<void> {}
41
+
42
+ public async authenticate(
43
+ request: Request,
44
+ sessionContext: SessionContext,
45
+ ): Promise<Session> {
46
+ const {api, config, logger} = this;
47
+ const {shop, session, sessionToken} = sessionContext;
48
+
49
+ if (!sessionToken) throw new InvalidJwtError();
50
+
51
+ if (!session || session.isExpired()) {
52
+ logger.info('No valid session found');
53
+ logger.info('Requesting offline access token');
54
+ const {session: offlineSession} = await this.exchangeToken({
55
+ request,
56
+ sessionToken,
57
+ shop,
58
+ requestedTokenType: RequestedTokenType.OfflineAccessToken,
59
+ });
60
+
61
+ await config.sessionStorage.storeSession(offlineSession);
62
+
63
+ let newSession = offlineSession;
64
+
65
+ if (config.useOnlineTokens) {
66
+ logger.info('Requesting online access token');
67
+ const {session: onlineSession} = await this.exchangeToken({
68
+ request,
69
+ sessionToken,
70
+ shop,
71
+ requestedTokenType: RequestedTokenType.OnlineAccessToken,
72
+ });
73
+
74
+ await config.sessionStorage.storeSession(onlineSession);
75
+ newSession = onlineSession;
76
+ }
77
+
78
+ try {
79
+ await this.handleAfterAuthHook(
80
+ {api, config, logger},
81
+ newSession,
82
+ request,
83
+ sessionToken,
84
+ );
85
+ } catch (error) {
86
+ throw new Response(undefined, {
87
+ status: 500,
88
+ statusText: 'Internal Server Error',
89
+ });
90
+ }
91
+
92
+ return newSession;
93
+ }
94
+
95
+ return session!;
96
+ }
97
+
98
+ public handleClientError(request: Request): HandleAdminClientError {
99
+ const {api, config, logger} = this;
100
+ return handleClientErrorFactory({
101
+ request,
102
+ onError: async ({session, error}: OnErrorOptions) => {
103
+ if (error.response.code === 401) {
104
+ config.sessionStorage.deleteSession(session.id);
105
+
106
+ respondToInvalidSessionToken({
107
+ params: {config, api, logger},
108
+ request,
109
+ });
110
+ }
111
+ },
112
+ });
113
+ }
114
+
115
+ private async exchangeToken({
116
+ request,
117
+ shop,
118
+ sessionToken,
119
+ requestedTokenType,
120
+ }: {
121
+ request: Request;
122
+ shop: string;
123
+ sessionToken: string;
124
+ requestedTokenType: RequestedTokenType;
125
+ }): Promise<{session: Session}> {
126
+ const {api, config, logger} = this;
127
+
128
+ try {
129
+ return await api.auth.tokenExchange({
130
+ sessionToken,
131
+ shop,
132
+ requestedTokenType,
133
+ });
134
+ } catch (error) {
135
+ if (
136
+ error instanceof InvalidJwtError ||
137
+ (error instanceof HttpResponseError &&
138
+ error.response.code === 400 &&
139
+ error.response.body?.error === 'invalid_subject_token')
140
+ ) {
141
+ throw respondToInvalidSessionToken({
142
+ params: {api, config, logger},
143
+ request,
144
+ retryRequest: true,
145
+ });
146
+ }
147
+
148
+ throw new Response(undefined, {
149
+ status: 500,
150
+ statusText: 'Internal Server Error',
151
+ });
152
+ }
153
+ }
154
+
155
+ private async handleAfterAuthHook(
156
+ params: BasicParams,
157
+ session: Session,
158
+ request: Request,
159
+ sessionToken: string,
160
+ ) {
161
+ const {config} = params;
162
+ await config.idempotentPromiseHandler.handlePromise({
163
+ promiseFunction: () => {
164
+ return triggerAfterAuthHook(params, session, request, this);
165
+ },
166
+ identifier: sessionToken,
167
+ });
168
+ }
169
+ }
@@ -0,0 +1,29 @@
1
+ import {HttpResponseError, Session} from '@shopify/shopify-api';
2
+
3
+ import {HandleAdminClientError} from '../../../clients';
4
+
5
+ export interface SessionContext {
6
+ shop: string;
7
+ session?: Session;
8
+ sessionToken?: string;
9
+ }
10
+
11
+ export interface OnErrorOptions {
12
+ request: Request;
13
+ session: Session;
14
+ error: HttpResponseError;
15
+ }
16
+
17
+ export interface HandleClientErrorOptions {
18
+ request: Request;
19
+ onError?: ({session, error}: OnErrorOptions) => Promise<void | never>;
20
+ }
21
+
22
+ export interface AuthorizationStrategy {
23
+ respondToOAuthRequests: (request: Request) => Promise<void | never>;
24
+ authenticate: (
25
+ request: Request,
26
+ sessionContext: SessionContext,
27
+ ) => Promise<Session | never>;
28
+ handleClientError: (request: Request) => HandleAdminClientError;
29
+ }
@@ -0,0 +1,199 @@
1
+ import {JwtPayload, Session, ShopifyRestResources} from '@shopify/shopify-api';
2
+
3
+ import {EnsureCORSFunction} from '../helpers/ensure-cors-headers';
4
+ import type {AppConfigArg} from '../../config-types';
5
+ import type {AdminApiContext} from '../../clients';
6
+
7
+ import type {BillingContext} from './billing/types';
8
+ import {RedirectFunction} from './helpers/redirect';
9
+
10
+ interface AdminContextInternal<
11
+ Config extends AppConfigArg,
12
+ Resources extends ShopifyRestResources = ShopifyRestResources,
13
+ > {
14
+ /**
15
+ * The session for the user who made the request.
16
+ *
17
+ * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.
18
+ *
19
+ * Use this to get shop or user-specific data.
20
+ *
21
+ * @example
22
+ * <caption>Using offline sessions.</caption>
23
+ * <description>Get your app's shop-specific data using an offline session.</description>
24
+ * ```ts
25
+ * // shopify.server.ts
26
+ * import { shopifyApp } from "@shopify/shopify-app-remix/server";
27
+ *
28
+ * const shopify = shopifyApp({
29
+ * // ...etc
30
+ * });
31
+ * export default shopify;
32
+ * export const authenticate = shopify.authenticate;
33
+ * ```
34
+ * ```ts
35
+ * // /app/routes/**\/*.ts
36
+ * import { LoaderFunctionArgs, json } from "@remix-run/node";
37
+ * import { authenticate } from "../shopify.server";
38
+ * import { getMyAppData } from "~/db/model.server";
39
+ *
40
+ * export const loader = async ({ request }: LoaderFunctionArgs) => {
41
+ * const { session } = await authenticate.admin(request);
42
+ * return json(await getMyAppData({shop: session.shop));
43
+ * };
44
+ * ```
45
+ *
46
+ * @example
47
+ * <caption>Using online sessions.</caption>
48
+ * <description>Get your app's user-specific data using an online session.</description>
49
+ * ```ts
50
+ * // shopify.server.ts
51
+ * import { shopifyApp } from "@shopify/shopify-app-remix/server";
52
+ *
53
+ * const shopify = shopifyApp({
54
+ * // ...etc
55
+ * useOnlineTokens: true,
56
+ * });
57
+ * export default shopify;
58
+ * export const authenticate = shopify.authenticate;
59
+ * ```
60
+ * ```ts
61
+ * // /app/routes/**\/*.ts
62
+ * import { LoaderFunctionArgs, json } from "@remix-run/node";
63
+ * import { authenticate } from "../shopify.server";
64
+ * import { getMyAppData } from "~/db/model.server";
65
+ *
66
+ * export const loader = async ({ request }: LoaderFunctionArgs) => {
67
+ * const { session } = await authenticate.admin(request);
68
+ * return json(await getMyAppData({user: session.onlineAccessInfo!.id}));
69
+ * };
70
+ * ```
71
+ */
72
+ session: Session;
73
+
74
+ /**
75
+ * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.
76
+ */
77
+ admin: AdminApiContext<Resources>;
78
+
79
+ /**
80
+ * Billing methods for this store, based on the plans defined in the `billing` config option.
81
+ *
82
+ * {@link https://shopify.dev/docs/apps/billing}
83
+ */
84
+ billing: BillingContext<Config>;
85
+
86
+ /**
87
+ * A function that ensures the CORS headers are set correctly for the response.
88
+ *
89
+ * @example
90
+ * <caption>Setting CORS headers for a admin request.</caption>
91
+ * <description>Use the `cors` helper to ensure your app can respond to requests from admin extensions.</description>
92
+ * ```ts
93
+ * // /app/routes/admin/my-route.ts
94
+ * import { LoaderFunctionArgs, json } from "@remix-run/node";
95
+ * import { authenticate } from "../shopify.server";
96
+ * import { getMyAppData } from "~/db/model.server";
97
+ *
98
+ * export const loader = async ({ request }: LoaderFunctionArgs) => {
99
+ * const { session, cors } = await authenticate.admin(request);
100
+ * return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));
101
+ * };
102
+ * ```
103
+ */
104
+ cors: EnsureCORSFunction;
105
+ }
106
+
107
+ export interface EmbeddedAdminContext<
108
+ Config extends AppConfigArg,
109
+ Resources extends ShopifyRestResources = ShopifyRestResources,
110
+ > extends AdminContextInternal<Config, Resources> {
111
+ /**
112
+ * The decoded and validated session token for the request.
113
+ *
114
+ * Returned only if `isEmbeddedApp` is `true`.
115
+ *
116
+ * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}
117
+ *
118
+ * @example
119
+ * <caption>Using the decoded session token.</caption>
120
+ * <description>Get user-specific data using the `sessionToken` object.</description>
121
+ * ```ts
122
+ * // shopify.server.ts
123
+ * import { shopifyApp } from "@shopify/shopify-app-remix/server";
124
+ *
125
+ * const shopify = shopifyApp({
126
+ * // ...etc
127
+ * useOnlineTokens: true,
128
+ * });
129
+ * export default shopify;
130
+ * export const authenticate = shopify.authenticate;
131
+ * ```
132
+ * ```ts
133
+ * // /app/routes/**\/*.ts
134
+ * import { LoaderFunctionArgs, json } from "@remix-run/node";
135
+ * import { authenticate } from "../shopify.server";
136
+ * import { getMyAppData } from "~/db/model.server";
137
+ *
138
+ * export const loader = async ({ request }: LoaderFunctionArgs) => {
139
+ * const { sessionToken } = await authenticate.public.checkout(
140
+ * request
141
+ * );
142
+ * return json(await getMyAppData({user: sessionToken.sub}));
143
+ * };
144
+ * ```
145
+ */
146
+ sessionToken: JwtPayload;
147
+
148
+ /**
149
+ * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded
150
+ * apps.
151
+ *
152
+ * Returned only if `isEmbeddedApp` is `true`.
153
+ *
154
+ * @example
155
+ * <caption>Redirecting to an app route.</caption>
156
+ * <description>Use the `redirect` helper to safely redirect between pages.</description>
157
+ * ```ts
158
+ * // /app/routes/admin/my-route.ts
159
+ * import { LoaderFunctionArgs, json } from "@remix-run/node";
160
+ * import { authenticate } from "../shopify.server";
161
+ *
162
+ * export const loader = async ({ request }: LoaderFunctionArgs) => {
163
+ * const { session, redirect } = await authenticate.admin(request);
164
+ * return redirect("/");
165
+ * };
166
+ * ```
167
+ *
168
+ * @example
169
+ * <caption>Redirecting outside of Shopify admin.</caption>
170
+ * <description>Pass in a `target` option of `_top` or `_parent` to go to an external URL.</description>
171
+ * ```ts
172
+ * // /app/routes/admin/my-route.ts
173
+ * import { LoaderFunctionArgs, json } from "@remix-run/node";
174
+ * import { authenticate } from "../shopify.server";
175
+ *
176
+ * export const loader = async ({ request }: LoaderFunctionArgs) => {
177
+ * const { session, redirect } = await authenticate.admin(request);
178
+ * return redirect("/", { target: '_parent' });
179
+ * };
180
+ * ```
181
+ */
182
+ redirect: RedirectFunction;
183
+ }
184
+ export interface NonEmbeddedAdminContext<
185
+ Config extends AppConfigArg,
186
+ Resources extends ShopifyRestResources = ShopifyRestResources,
187
+ > extends AdminContextInternal<Config, Resources> {}
188
+
189
+ export type AdminContext<
190
+ Config extends AppConfigArg,
191
+ Resources extends ShopifyRestResources = ShopifyRestResources,
192
+ > = Config['isEmbeddedApp'] extends false
193
+ ? NonEmbeddedAdminContext<Config, Resources>
194
+ : EmbeddedAdminContext<Config, Resources>;
195
+
196
+ export type AuthenticateAdmin<
197
+ Config extends AppConfigArg,
198
+ Resources extends ShopifyRestResources = ShopifyRestResources,
199
+ > = (request: Request) => Promise<AdminContext<Config, Resources>>;
@@ -0,0 +1,15 @@
1
+ export const APP_BRIDGE_URL =
2
+ 'https://cdn.shopify.com/shopifycloud/app-bridge.js';
3
+
4
+ export const REAUTH_URL_HEADER =
5
+ 'X-Shopify-API-Request-Failure-Reauthorize-Url';
6
+
7
+ export const RETRY_INVALID_SESSION_HEADER = {
8
+ 'X-Shopify-Retry-Invalid-Session-Request': '1',
9
+ };
10
+
11
+ export const CORS_HEADERS = {
12
+ 'Access-Control-Allow-Origin': '*',
13
+ 'Access-Control-Allow-Headers': 'Authorization',
14
+ 'Access-Control-Expose-Headers': REAUTH_URL_HEADER,
15
+ };
@@ -0,0 +1,142 @@
1
+ import {SessionStorage} from '@shopify/shopify-app-session-storage';
2
+ import {MemorySessionStorage} from '@shopify/shopify-app-session-storage-memory';
3
+
4
+ import {shopifyApp} from '../../..';
5
+ import {
6
+ expectAdminApiClient,
7
+ getHmac,
8
+ getThrownResponse,
9
+ setUpValidSession,
10
+ testConfig,
11
+ } from '../../../__test-helpers';
12
+
13
+ const FLOW_URL = 'https://example.myapp.io/authenticate/flow';
14
+
15
+ describe('authenticating flow requests', () => {
16
+ it('throws a 405 response if the request method is not POST', async () => {
17
+ // GIVEN
18
+ const shopify = shopifyApp(testConfig());
19
+
20
+ // WHEN
21
+ const response = await getThrownResponse(
22
+ shopify.authenticate.flow,
23
+ new Request(FLOW_URL, {method: 'GET'}),
24
+ );
25
+
26
+ // THEN
27
+ expect(response.status).toBe(405);
28
+ expect(response.statusText).toBe('Method not allowed');
29
+ });
30
+
31
+ it('throws a 400 response if the is missing the HMAC signature', async () => {
32
+ // GIVEN
33
+ const shopify = shopifyApp(testConfig());
34
+
35
+ // WHEN
36
+ const response = await getThrownResponse(
37
+ shopify.authenticate.flow,
38
+ new Request(FLOW_URL, {method: 'POST'}),
39
+ );
40
+
41
+ // THEN
42
+ expect(response.status).toBe(400);
43
+ expect(response.statusText).toBe('Bad Request');
44
+ });
45
+
46
+ it('throws a 400 response if the request has an invalid HMAC signature', async () => {
47
+ // GIVEN
48
+ const shopify = shopifyApp(testConfig());
49
+
50
+ // WHEN
51
+ const response = await getThrownResponse(
52
+ shopify.authenticate.flow,
53
+ new Request(FLOW_URL, {
54
+ method: 'POST',
55
+ headers: {
56
+ 'X-Shopify-Hmac-Sha256': 'not-the-right-signature',
57
+ },
58
+ }),
59
+ );
60
+
61
+ // THEN
62
+ expect(response.status).toBe(400);
63
+ expect(response.statusText).toBe('Bad Request');
64
+ });
65
+
66
+ it('throws a 400 response if there is no session for the shop', async () => {
67
+ // GIVEN
68
+ const shopify = shopifyApp(testConfig());
69
+ const body = {shopify_domain: 'not-the-right-shop.myshopify.io'};
70
+
71
+ // WHEN
72
+ const response = await getThrownResponse(
73
+ shopify.authenticate.flow,
74
+ new Request(FLOW_URL, {
75
+ body: JSON.stringify(body),
76
+ method: 'POST',
77
+ headers: {
78
+ 'X-Shopify-Hmac-Sha256': getHmac(JSON.stringify(body)),
79
+ },
80
+ }),
81
+ );
82
+
83
+ // THEN
84
+ expect(response.status).toBe(400);
85
+ expect(response.statusText).toBe('Bad Request');
86
+ });
87
+
88
+ it('valid requests with a session succeed', async () => {
89
+ // GIVEN
90
+ const sessionStorage = new MemorySessionStorage();
91
+ const shopify = shopifyApp(testConfig({sessionStorage}));
92
+
93
+ const {
94
+ request,
95
+ body,
96
+ session: expectedSession,
97
+ } = await getValidRequest(sessionStorage);
98
+
99
+ // WHEN
100
+ const {payload, session} = await shopify.authenticate.flow(request);
101
+
102
+ // THEN
103
+ expect(session).toEqual(expectedSession);
104
+ expect(payload).toEqual(body);
105
+ });
106
+
107
+ describe('valid requests include an API client object', () => {
108
+ expectAdminApiClient(async () => {
109
+ const sessionStorage = new MemorySessionStorage();
110
+ const shopify = shopifyApp(testConfig({sessionStorage}));
111
+
112
+ const {request, session: expectedSession} =
113
+ await getValidRequest(sessionStorage);
114
+
115
+ const {admin, session: actualSession} =
116
+ await shopify.authenticate.flow(request);
117
+
118
+ if (!admin) {
119
+ throw new Error('No admin client');
120
+ }
121
+
122
+ return {admin, expectedSession, actualSession};
123
+ });
124
+ });
125
+ });
126
+
127
+ async function getValidRequest(sessionStorage: SessionStorage) {
128
+ const session = await setUpValidSession(sessionStorage, {isOnline: false});
129
+
130
+ const body = {shopify_domain: session.shop};
131
+ const bodyString = JSON.stringify(body);
132
+
133
+ const request = new Request(FLOW_URL, {
134
+ body: bodyString,
135
+ method: 'POST',
136
+ headers: {
137
+ 'X-Shopify-Hmac-Sha256': getHmac(bodyString),
138
+ },
139
+ });
140
+
141
+ return {body, request, session};
142
+ }
@@ -0,0 +1,34 @@
1
+ import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs';
2
+
3
+ const data: ReferenceEntityTemplateSchema = {
4
+ name: 'Flow',
5
+ description:
6
+ 'Contains functions for verifying Shopify Flow extensions.' +
7
+ '\n\nSee the [Flow documentation](https://shopify.dev/docs/apps/flow/actions/endpoints) for more information.',
8
+ category: 'Authenticate',
9
+ type: 'object',
10
+ isVisualComponent: false,
11
+ definitions: [
12
+ {
13
+ title: 'authenticate.flow',
14
+ description: 'Verifies requests coming from Shopify Flow extensions.',
15
+ type: 'AuthenticateFlow',
16
+ },
17
+ ],
18
+ jsDocTypeExamples: ['FlowContext'],
19
+ related: [
20
+ {
21
+ name: 'Admin API context',
22
+ subtitle: 'Interact with the Admin API.',
23
+ url: '/docs/api/shopify-app-remix/apis/admin-api',
24
+ },
25
+ {
26
+ name: 'Flow action endpoints',
27
+ subtitle: 'Receive requests from Flow.',
28
+ url: '/docs/apps/flow/actions/endpoints',
29
+ type: 'shopify',
30
+ },
31
+ ],
32
+ };
33
+
34
+ export default data;
@@ -0,0 +1,71 @@
1
+ import {ShopifyRestResources} from '@shopify/shopify-api';
2
+
3
+ import {adminClientFactory} from '../../clients/admin';
4
+ import {BasicParams} from '../../types';
5
+
6
+ import type {AuthenticateFlow, FlowContext} from './types';
7
+
8
+ export function authenticateFlowFactory<
9
+ Resources extends ShopifyRestResources = ShopifyRestResources,
10
+ >(params: BasicParams): AuthenticateFlow<Resources> {
11
+ const {api, config, logger} = params;
12
+
13
+ return async function authenticate(
14
+ request: Request,
15
+ ): Promise<FlowContext<Resources>> {
16
+ logger.info('Authenticating flow request');
17
+
18
+ if (request.method !== 'POST') {
19
+ logger.debug(
20
+ 'Received a non-POST request for flow. Only POST requests are allowed.',
21
+ {url: request.url, method: request.method},
22
+ );
23
+ throw new Response(undefined, {
24
+ status: 405,
25
+ statusText: 'Method not allowed',
26
+ });
27
+ }
28
+
29
+ const rawBody = await request.text();
30
+ const result = await api.flow.validate({
31
+ rawBody,
32
+ rawRequest: request,
33
+ });
34
+
35
+ if (!result.valid) {
36
+ logger.error('Received an invalid flow request', {reason: result.reason});
37
+
38
+ throw new Response(undefined, {
39
+ status: 400,
40
+ statusText: 'Bad Request',
41
+ });
42
+ }
43
+
44
+ const payload = JSON.parse(rawBody);
45
+
46
+ logger.debug('Flow request is valid, looking for an offline session', {
47
+ shop: payload.shopify_domain,
48
+ });
49
+
50
+ const sessionId = api.session.getOfflineId(payload.shopify_domain);
51
+ const session = await config.sessionStorage.loadSession(sessionId);
52
+
53
+ if (!session) {
54
+ logger.info('Flow request could not find session', {
55
+ shop: payload.shopify_domain,
56
+ });
57
+ throw new Response(undefined, {
58
+ status: 400,
59
+ statusText: 'Bad Request',
60
+ });
61
+ }
62
+
63
+ logger.debug('Found a session for the flow request', {shop: session.shop});
64
+
65
+ return {
66
+ session,
67
+ payload,
68
+ admin: adminClientFactory<Resources>({params, session}),
69
+ };
70
+ };
71
+ }