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,90 @@
1
+ import {Session, ShopifyRestResources} from '@shopify/shopify-api';
2
+
3
+ import type {AdminApiContext} from '../../clients';
4
+
5
+ export interface FlowContext<
6
+ Resources extends ShopifyRestResources = ShopifyRestResources,
7
+ > {
8
+ /**
9
+ * A session with an offline token for the shop.
10
+ *
11
+ * Returned only if there is a session for the shop.
12
+ *
13
+ * @example
14
+ * <caption>Shopify session for the Flow request.</caption>
15
+ * <description>Use the session associated with this request to use REST resources.</description>
16
+ * ```ts
17
+ * // /app/routes/flow.tsx
18
+ * import { ActionFunction } from "@remix-run/node";
19
+ * import { authenticate } from "../shopify.server";
20
+ *
21
+ * export const action: ActionFunction = async ({ request }) => {
22
+ * const { session, admin } = await authenticate.flow(request);
23
+ *
24
+ * const products = await admin?.rest.resources.Product.all({ session });
25
+ * // Use products
26
+ *
27
+ * return new Response();
28
+ * };
29
+ * ```
30
+ */
31
+ session: Session;
32
+
33
+ /**
34
+ * The payload from the Flow request.
35
+ *
36
+ * @example
37
+ * <caption>Flow payload.</caption>
38
+ * <description>Get the request's POST payload.</description>
39
+ * ```ts
40
+ * // /app/routes/flow.tsx
41
+ * import { ActionFunction } from "@remix-run/node";
42
+ * import { authenticate } from "../shopify.server";
43
+ *
44
+ * export const action: ActionFunction = async ({ request }) => {
45
+ * const { payload } = await authenticate.flow(request);
46
+ * return new Response();
47
+ * };
48
+ * ```
49
+ */
50
+ payload: any;
51
+
52
+ /**
53
+ * An admin context for the Flow request.
54
+ *
55
+ * Returned only if there is a session for the shop.
56
+ *
57
+ * @example
58
+ * <caption>Flow admin context.</caption>
59
+ * <description>Use the `admin` object in the context to interact with the Admin API.</description>
60
+ * ```ts
61
+ * // /app/routes/flow.tsx
62
+ * import { ActionFunctionArgs } from "@remix-run/node";
63
+ * import { authenticate } from "../shopify.server";
64
+ *
65
+ * export async function action({ request }: ActionFunctionArgs) {
66
+ * const { admin } = await authenticate.flow(request);
67
+ *
68
+ * const response = await admin?.graphql(
69
+ * `#graphql
70
+ * mutation populateProduct($input: ProductInput!) {
71
+ * productCreate(input: $input) {
72
+ * product {
73
+ * id
74
+ * }
75
+ * }
76
+ * }`,
77
+ * { variables: { input: { title: "Product Name" } } }
78
+ * );
79
+ *
80
+ * const productData = await response.json();
81
+ * return json({ data: productData.data });
82
+ * }
83
+ * ```
84
+ */
85
+ admin: AdminApiContext<Resources>;
86
+ }
87
+
88
+ export type AuthenticateFlow<
89
+ Resources extends ShopifyRestResources = ShopifyRestResources,
90
+ > = (request: Request) => Promise<FlowContext<Resources>>;
@@ -0,0 +1,25 @@
1
+ import {shopifyApp} from '../../..';
2
+ import {
3
+ APP_URL,
4
+ TEST_SHOP,
5
+ expectDocumentRequestHeaders,
6
+ testConfig,
7
+ } from '../../../__test-helpers';
8
+
9
+ describe('addDocumentResponseHeaders', () => {
10
+ it.each([true, false])(
11
+ 'adds frame-ancestors CSP headers when embedded = %s',
12
+ (isEmbeddedApp) => {
13
+ // GIVEN
14
+ const shopify = shopifyApp(testConfig({isEmbeddedApp}));
15
+ const request = new Request(`${APP_URL}?shop=${TEST_SHOP}`);
16
+ const response = new Response();
17
+
18
+ // WHEN
19
+ shopify.addDocumentResponseHeaders(request, response.headers);
20
+
21
+ // THEN
22
+ expectDocumentRequestHeaders(response, isEmbeddedApp);
23
+ },
24
+ );
25
+ });
@@ -0,0 +1,21 @@
1
+ import {APP_BRIDGE_URL} from '../../const';
2
+ import {appBridgeUrl, setAppBridgeUrlOverride} from '../app-bridge-url';
3
+
4
+ describe('appBridgeUrl', () => {
5
+ it('defaults to returning the const APP_BRIDGE_URL', () => {
6
+ // GIVEN
7
+ setAppBridgeUrlOverride(undefined as any as string);
8
+
9
+ // THEN
10
+ expect(appBridgeUrl()).toEqual(APP_BRIDGE_URL);
11
+ });
12
+
13
+ it('returns the override value when set', () => {
14
+ // GIVEN
15
+ const override = 'http://localhost:9876/app-bridge.js';
16
+ setAppBridgeUrlOverride(override);
17
+
18
+ // THEN
19
+ expect(appBridgeUrl()).toEqual(override);
20
+ });
21
+ });
@@ -0,0 +1,104 @@
1
+ import {ShopifyError} from '@shopify/shopify-api';
2
+
3
+ import {IdempotentPromiseHandler} from '../idempotent-promise-handler';
4
+
5
+ const mockFunction = jest.fn();
6
+ async function promiseFunction() {
7
+ mockFunction();
8
+ }
9
+
10
+ afterEach(() => {
11
+ mockFunction.mockReset();
12
+ });
13
+
14
+ describe('IdempotentPromiseHandler', () => {
15
+ it('runs the promise function only once for the same identifier', async () => {
16
+ // GIVEN
17
+ const promiseHandler = new IdempotentPromiseHandler();
18
+
19
+ // WHEN
20
+ promiseHandler.handlePromise({
21
+ promiseFunction,
22
+ identifier: 'first-promise',
23
+ });
24
+
25
+ promiseHandler.handlePromise({
26
+ promiseFunction,
27
+ identifier: 'first-promise',
28
+ });
29
+
30
+ // THEN
31
+ expect(mockFunction).toHaveBeenCalledTimes(1);
32
+ });
33
+
34
+ it('runs the promise function once for each identifier', async () => {
35
+ // GIVEN
36
+ // const promiseFunction = jest.fn();
37
+ const promiseHandler = new IdempotentPromiseHandler();
38
+
39
+ // WHEN
40
+ promiseHandler.handlePromise({
41
+ promiseFunction,
42
+ identifier: 'first-promise',
43
+ });
44
+
45
+ promiseHandler.handlePromise({
46
+ promiseFunction,
47
+ identifier: 'second-promise',
48
+ });
49
+
50
+ // THEN
51
+ expect(mockFunction).toHaveBeenCalledTimes(2);
52
+ });
53
+
54
+ it('clears stale identifier from hash', async () => {
55
+ // GIVEN
56
+ const currentDate = Date.now();
57
+ jest.useFakeTimers().setSystemTime(currentDate);
58
+ const promiseHandler = new IdempotentPromiseHandler() as any;
59
+
60
+ // WHEN
61
+ promiseHandler.handlePromise({
62
+ promiseFunction,
63
+ identifier: 'old-promise',
64
+ });
65
+
66
+ jest.useFakeTimers().setSystemTime(currentDate + 70000);
67
+
68
+ await promiseHandler.handlePromise({
69
+ promiseFunction,
70
+ identifier: 'new-promise',
71
+ });
72
+
73
+ expect(promiseHandler.identifiers.size).toBe(1);
74
+ });
75
+
76
+ it('clears stale identifier from hash even when promise fails', async () => {
77
+ // GIVEN
78
+ const promiseFunctionErr = () => {
79
+ throw new ShopifyError();
80
+ };
81
+ const currentDate = Date.now();
82
+ jest.useFakeTimers().setSystemTime(currentDate);
83
+ const promiseHandler = new IdempotentPromiseHandler() as any;
84
+
85
+ // WHEN
86
+ expect(
87
+ promiseHandler.handlePromise({
88
+ promiseFunction: promiseFunctionErr,
89
+ identifier: 'old-promise',
90
+ }),
91
+ ).rejects.toThrow();
92
+
93
+ jest.useFakeTimers().setSystemTime(currentDate + 70000);
94
+
95
+ expect(
96
+ promiseHandler.handlePromise({
97
+ promiseFunction: promiseFunctionErr,
98
+ identifier: 'new-promise',
99
+ }),
100
+ ).rejects.toThrow();
101
+
102
+ expect(promiseHandler.identifiers.size).toBe(1);
103
+ });
104
+ });
@@ -0,0 +1,43 @@
1
+ import type {BasicParams} from '../../types';
2
+
3
+ export type AddDocumentResponseHeadersFunction = (
4
+ request: Request,
5
+ headers: Headers,
6
+ ) => void;
7
+
8
+ export function addDocumentResponseHeadersFactory(
9
+ params: BasicParams,
10
+ ): AddDocumentResponseHeadersFunction {
11
+ const {api, config} = params;
12
+
13
+ return function (request: Request, headers: Headers) {
14
+ const {searchParams} = new URL(request.url);
15
+ const shop = api.utils.sanitizeShop(searchParams.get('shop')!);
16
+
17
+ addDocumentResponseHeaders(headers, config.isEmbeddedApp, shop);
18
+ };
19
+ }
20
+
21
+ export function addDocumentResponseHeaders(
22
+ headers: Headers,
23
+ isEmbeddedApp: boolean,
24
+ shop: string | null | undefined,
25
+ ) {
26
+ if (shop) {
27
+ headers.set(
28
+ 'Link',
29
+ '<https://cdn.shopify.com/shopifycloud/app-bridge.js>; rel="preload"; as="script";',
30
+ );
31
+ }
32
+
33
+ if (isEmbeddedApp) {
34
+ if (shop) {
35
+ headers.set(
36
+ 'Content-Security-Policy',
37
+ `frame-ancestors https://${shop} https://admin.shopify.com https://*.spin.dev;`,
38
+ );
39
+ }
40
+ } else {
41
+ headers.set('Content-Security-Policy', `frame-ancestors 'none';`);
42
+ }
43
+ }
@@ -0,0 +1,10 @@
1
+ import {APP_BRIDGE_URL} from '../const';
2
+
3
+ let appBridgeUrlOverride: string | undefined;
4
+ export function setAppBridgeUrlOverride(url: string) {
5
+ appBridgeUrlOverride = url;
6
+ }
7
+
8
+ export function appBridgeUrl() {
9
+ return appBridgeUrlOverride || APP_BRIDGE_URL;
10
+ }
@@ -0,0 +1,38 @@
1
+ import {BasicParams} from '../../types';
2
+ import {REAUTH_URL_HEADER} from '../const';
3
+
4
+ export interface EnsureCORSFunction {
5
+ (response: Response): Response;
6
+ }
7
+
8
+ export function ensureCORSHeadersFactory(
9
+ params: BasicParams,
10
+ request: Request,
11
+ corsHeaders: string[] = [],
12
+ ): EnsureCORSFunction {
13
+ const {logger, config} = params;
14
+
15
+ return function ensureCORSHeaders(response) {
16
+ const origin = request.headers.get('Origin');
17
+ if (origin && origin !== config.appUrl) {
18
+ logger.debug(
19
+ 'Request comes from a different origin, adding CORS headers',
20
+ );
21
+
22
+ const corsHeadersSet = new Set([
23
+ 'Authorization',
24
+ 'Content-Type',
25
+ ...corsHeaders,
26
+ ]);
27
+
28
+ response.headers.set('Access-Control-Allow-Origin', '*');
29
+ response.headers.set(
30
+ 'Access-Control-Allow-Headers',
31
+ [...corsHeadersSet].join(', '),
32
+ );
33
+ response.headers.set('Access-Control-Expose-Headers', REAUTH_URL_HEADER);
34
+ }
35
+
36
+ return response;
37
+ };
38
+ }
@@ -0,0 +1,11 @@
1
+ const SESSION_TOKEN_PARAM = 'id_token';
2
+
3
+ export function getSessionTokenHeader(request: Request): string | undefined {
4
+ return request.headers.get('authorization')?.replace('Bearer ', '');
5
+ }
6
+
7
+ export function getSessionTokenFromUrlParam(request: Request): string | null {
8
+ const url = new URL(request.url);
9
+
10
+ return url.searchParams.get(SESSION_TOKEN_PARAM);
11
+ }
@@ -0,0 +1,45 @@
1
+ export interface IdempotentHandlePromiseParams {
2
+ promiseFunction: () => Promise<any>;
3
+ identifier: string;
4
+ }
5
+
6
+ const IDENTIFIER_TTL_MS = 60000;
7
+
8
+ export class IdempotentPromiseHandler {
9
+ protected identifiers: Map<string, number>;
10
+
11
+ constructor() {
12
+ this.identifiers = new Map<string, number>();
13
+ }
14
+
15
+ async handlePromise({
16
+ promiseFunction,
17
+ identifier,
18
+ }: IdempotentHandlePromiseParams): Promise<any> {
19
+ try {
20
+ if (this.isPromiseRunnable(identifier)) {
21
+ await promiseFunction();
22
+ }
23
+ } finally {
24
+ this.clearStaleIdentifiers();
25
+ }
26
+
27
+ return Promise.resolve();
28
+ }
29
+
30
+ private isPromiseRunnable(identifier: string) {
31
+ if (!this.identifiers.has(identifier)) {
32
+ this.identifiers.set(identifier, Date.now());
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+
38
+ private async clearStaleIdentifiers() {
39
+ this.identifiers.forEach((date, identifier, map) => {
40
+ if (Date.now() - date > IDENTIFIER_TTL_MS) {
41
+ map.delete(identifier);
42
+ }
43
+ });
44
+ }
45
+ }
@@ -0,0 +1,8 @@
1
+ export * from './app-bridge-url';
2
+ export * from './add-response-headers';
3
+ export * from './ensure-cors-headers';
4
+ export * from './validate-session-token';
5
+ export * from './get-session-token-header';
6
+ export * from './reject-bot-request';
7
+ export * from './respond-to-options-request';
8
+ export * from './respond-to-invalid-session-token';
@@ -0,0 +1,13 @@
1
+ import isbot from 'isbot';
2
+
3
+ import type {BasicParams} from '../../types';
4
+
5
+ export function respondToBotRequest(
6
+ {logger}: BasicParams,
7
+ request: Request,
8
+ ): void | never {
9
+ if (isbot(request.headers.get('User-Agent'))) {
10
+ logger.debug('Request is from a bot, skipping auth');
11
+ throw new Response(undefined, {status: 410, statusText: 'Gone'});
12
+ }
13
+ }
@@ -0,0 +1,29 @@
1
+ import {BasicParams} from 'src/server/types';
2
+
3
+ import {redirectToBouncePage} from '../admin/helpers/redirect-to-bounce-page';
4
+ import {RETRY_INVALID_SESSION_HEADER} from '../const';
5
+
6
+ interface RespondToInvalidSessionTokenParams {
7
+ params: BasicParams;
8
+ request: Request;
9
+ retryRequest?: boolean;
10
+ }
11
+
12
+ export function respondToInvalidSessionToken({
13
+ params,
14
+ request,
15
+ retryRequest = false,
16
+ }: RespondToInvalidSessionTokenParams) {
17
+ const {api, logger, config} = params;
18
+
19
+ const isDocumentRequest = !request.headers.get('authorization');
20
+ if (isDocumentRequest) {
21
+ return redirectToBouncePage({api, logger, config}, new URL(request.url));
22
+ }
23
+
24
+ throw new Response(undefined, {
25
+ status: 401,
26
+ statusText: 'Unauthorized',
27
+ headers: retryRequest ? RETRY_INVALID_SESSION_HEADER : {},
28
+ });
29
+ }
@@ -0,0 +1,26 @@
1
+ import {BasicParams} from '../../types';
2
+
3
+ import {ensureCORSHeadersFactory} from './ensure-cors-headers';
4
+
5
+ export function respondToOptionsRequest(
6
+ params: BasicParams,
7
+ request: Request,
8
+ corsHeaders?: string[],
9
+ ) {
10
+ if (request.method === 'OPTIONS') {
11
+ const ensureCORSHeaders = ensureCORSHeadersFactory(
12
+ params,
13
+ request,
14
+ corsHeaders,
15
+ );
16
+
17
+ throw ensureCORSHeaders(
18
+ new Response(null, {
19
+ status: 204,
20
+ headers: {
21
+ 'Access-Control-Max-Age': '7200',
22
+ },
23
+ }),
24
+ );
25
+ }
26
+ }
@@ -0,0 +1,32 @@
1
+ import {JwtPayload} from '@shopify/shopify-api';
2
+
3
+ import type {BasicParams} from '../../types';
4
+
5
+ interface ValidateSessionTokenOptions {
6
+ checkAudience?: boolean;
7
+ }
8
+
9
+ export async function validateSessionToken(
10
+ {api, logger}: BasicParams,
11
+ token: string,
12
+ {checkAudience = true}: ValidateSessionTokenOptions = {},
13
+ ): Promise<JwtPayload> {
14
+ logger.debug('Validating session token');
15
+
16
+ try {
17
+ const payload = await api.session.decodeSessionToken(token, {
18
+ checkAudience,
19
+ });
20
+ logger.debug('Session token is valid', {
21
+ payload: JSON.stringify(payload),
22
+ });
23
+
24
+ return payload;
25
+ } catch (error) {
26
+ logger.debug(`Failed to validate session token: ${error.message}`);
27
+ throw new Response(undefined, {
28
+ status: 401,
29
+ statusText: 'Unauthorized',
30
+ });
31
+ }
32
+ }
@@ -0,0 +1,157 @@
1
+ import {LoginErrorType, shopifyApp} from '../../../index';
2
+ import {
3
+ APP_URL,
4
+ TEST_SHOP,
5
+ TEST_SHOP_NAME,
6
+ getThrownResponse,
7
+ testConfig,
8
+ } from '../../../__test-helpers';
9
+
10
+ describe('login helper', () => {
11
+ it('returns an empty errors object if GET and no shop param', async () => {
12
+ // GIVEN
13
+ const shopify = shopifyApp(testConfig());
14
+ const requestMock = {
15
+ url: `${APP_URL}/auth/login`,
16
+ method: 'GET',
17
+ };
18
+
19
+ // WHEN
20
+ const errors = await shopify.login(requestMock as any as Request);
21
+
22
+ // THEN
23
+ expect(errors).toStrictEqual({});
24
+ });
25
+
26
+ it('does not access formData if method is GET', async () => {
27
+ // GIVEN
28
+ const formDataMock = jest.fn();
29
+ const shopify = shopifyApp(testConfig());
30
+ const requestMock = {
31
+ url: `${APP_URL}/auth/login?shop=${TEST_SHOP}`,
32
+ method: 'GET',
33
+ formData: formDataMock,
34
+ };
35
+
36
+ // WHEN
37
+ getThrownResponse(shopify.login, requestMock as any as Request);
38
+
39
+ // THEN
40
+ expect(formDataMock).not.toHaveBeenCalled();
41
+ });
42
+
43
+ it('returns an error if the shop parameter is missing', async () => {
44
+ // GIVEN
45
+ const shopify = shopifyApp(testConfig());
46
+ const requestMock = {
47
+ url: `${APP_URL}/auth/login`,
48
+ formData: async () => ({get: () => null}),
49
+ method: 'POST',
50
+ };
51
+
52
+ // WHEN
53
+ const errors = await shopify.login(requestMock as any as Request);
54
+
55
+ // THEN
56
+ expect(errors).toEqual({shop: LoginErrorType.MissingShop});
57
+ });
58
+
59
+ it.each([
60
+ {urlShop: 'invalid.shop', formShop: null, method: 'GET'},
61
+ {urlShop: null, formShop: 'invalid.shop', method: 'POST'},
62
+ ])(
63
+ 'returns an error if the shop parameter is invalid: %s',
64
+ async ({urlShop, formShop, method}) => {
65
+ // GIVEN
66
+ const shopify = shopifyApp(testConfig());
67
+ const requestMock = {
68
+ url: urlShop
69
+ ? `${APP_URL}/auth/login?shop=${urlShop}`
70
+ : `${APP_URL}/auth/login`,
71
+ formData: async () => ({get: () => formShop}),
72
+ method,
73
+ };
74
+
75
+ // WHEN
76
+ const errors = await shopify.login(requestMock as any as Request);
77
+
78
+ // THEN
79
+ expect(errors).toEqual({shop: LoginErrorType.InvalidShop});
80
+ },
81
+ );
82
+
83
+ describe.each([
84
+ {isEmbeddedApp: false, futureFlag: false, redirectToInstall: false},
85
+ {isEmbeddedApp: true, futureFlag: false, redirectToInstall: false},
86
+ {isEmbeddedApp: false, futureFlag: true, redirectToInstall: false},
87
+ {isEmbeddedApp: true, futureFlag: true, redirectToInstall: true},
88
+ ])('Given setup: %s', (testCaseConfig) => {
89
+ it.each([
90
+ {urlShop: null, formShop: TEST_SHOP, method: 'POST'},
91
+ {urlShop: TEST_SHOP, formShop: null, method: 'GET'},
92
+ {urlShop: null, formShop: 'test-shop', method: 'POST'},
93
+ {urlShop: 'test-shop', formShop: null, method: 'GET'},
94
+ {urlShop: null, formShop: 'test-shop.myshopify.com', method: 'POST'},
95
+ {urlShop: 'test-shop.myshopify.com', formShop: null, method: 'GET'},
96
+ ])(
97
+ 'returns a redirect to auth or install if the shop is valid: %s',
98
+ async ({urlShop, formShop, method}) => {
99
+ // GIVEN
100
+ const config = testConfig({
101
+ future: {unstable_newEmbeddedAuthStrategy: testCaseConfig.futureFlag},
102
+ isEmbeddedApp: testCaseConfig.isEmbeddedApp,
103
+ });
104
+ const shopify = shopifyApp(config);
105
+ const requestMock = {
106
+ url: urlShop
107
+ ? `${APP_URL}/auth/login?shop=${urlShop}`
108
+ : `${APP_URL}/auth/login`,
109
+ formData: async () => ({get: () => formShop}),
110
+ method,
111
+ };
112
+
113
+ // WHEN
114
+ const response = await getThrownResponse(
115
+ shopify.login,
116
+ requestMock as any as Request,
117
+ );
118
+
119
+ // THEN
120
+ const expectedPath = testCaseConfig.redirectToInstall
121
+ ? `https://admin.shopify.com/store/${TEST_SHOP_NAME}/oauth/install?client_id=${config.apiKey}`
122
+ : `${APP_URL}/auth?shop=${TEST_SHOP}`;
123
+
124
+ expect(response.status).toEqual(302);
125
+ expect(response.headers.get('location')).toEqual(expectedPath);
126
+ },
127
+ );
128
+
129
+ it('sanitizes the shop parameter', async () => {
130
+ // GIVEN
131
+ const config = testConfig({
132
+ future: {unstable_newEmbeddedAuthStrategy: testCaseConfig.futureFlag},
133
+ isEmbeddedApp: testCaseConfig.isEmbeddedApp,
134
+ });
135
+ const shopify = shopifyApp(config);
136
+ const requestMock = {
137
+ url: `${APP_URL}/auth/login`,
138
+ formData: async () => ({get: () => `https://${TEST_SHOP}/`}),
139
+ method: 'POST',
140
+ };
141
+
142
+ // WHEN
143
+ const response = await getThrownResponse(
144
+ shopify.login,
145
+ requestMock as any as Request,
146
+ );
147
+
148
+ const expectedPath = testCaseConfig.redirectToInstall
149
+ ? `https://admin.shopify.com/store/${TEST_SHOP_NAME}/oauth/install?client_id=${config.apiKey}`
150
+ : `${APP_URL}/auth?shop=${TEST_SHOP}`;
151
+
152
+ // THEN
153
+ expect(response.status).toEqual(302);
154
+ expect(response.headers.get('location')).toEqual(expectedPath);
155
+ });
156
+ });
157
+ });