@shopify/shopify-app-remix 2.6.1 → 2.8.0

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.
Files changed (129) hide show
  1. package/build/cjs/react/components/AppProvider/AppProvider.js +2 -0
  2. package/build/cjs/react/components/AppProxyForm/AppProxyForm.js +80 -0
  3. package/build/cjs/react/components/AppProxyLink/AppProxyLink.js +56 -0
  4. package/build/cjs/react/components/AppProxyProvider/AppProxyProvider.js +79 -0
  5. package/build/cjs/react/index.js +7 -0
  6. package/build/cjs/server/adapters/node/index.js +1 -1
  7. package/build/cjs/server/authenticate/admin/authenticate.js +1 -5
  8. package/build/cjs/server/authenticate/admin/strategies/token-exchange.js +1 -1
  9. package/build/cjs/server/authenticate/fulfillment-service/authenticate.js +70 -0
  10. package/build/cjs/server/authenticate/helpers/validate-session-token.js +11 -7
  11. package/build/cjs/server/authenticate/public/appProxy/authenticate.js +49 -20
  12. package/build/cjs/server/authenticate/public/checkout/authenticate.js +1 -1
  13. package/build/cjs/server/boundary/index.js +1 -1
  14. package/build/cjs/server/future/flags.js +28 -0
  15. package/build/cjs/server/shopify-app.js +9 -4
  16. package/build/cjs/server/types.js +0 -4
  17. package/build/cjs/server/version.js +1 -1
  18. package/build/ts/react/components/AppProvider/AppProvider.d.ts +2 -0
  19. package/build/ts/react/components/AppProvider/AppProvider.d.ts.map +1 -1
  20. package/build/ts/react/components/AppProvider/AppProvider.js +2 -0
  21. package/build/ts/react/components/AppProvider/AppProvider.js.map +1 -1
  22. package/build/ts/react/components/AppProxyForm/AppProxyForm.d.ts +59 -0
  23. package/build/ts/react/components/AppProxyForm/AppProxyForm.d.ts.map +1 -0
  24. package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts +4 -0
  25. package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts.map +1 -0
  26. package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.js +28 -0
  27. package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.js.map +1 -0
  28. package/build/ts/react/components/AppProxyForm/AppProxyForm.js +71 -0
  29. package/build/ts/react/components/AppProxyForm/AppProxyForm.js.map +1 -0
  30. package/build/ts/react/components/AppProxyForm/index.d.ts +2 -0
  31. package/build/ts/react/components/AppProxyForm/index.d.ts.map +1 -0
  32. package/build/ts/react/components/AppProxyForm/index.js +6 -0
  33. package/build/ts/react/components/AppProxyForm/index.js.map +1 -0
  34. package/build/ts/react/components/AppProxyLink/AppProxyLink.d.ts +36 -0
  35. package/build/ts/react/components/AppProxyLink/AppProxyLink.d.ts.map +1 -0
  36. package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts +4 -0
  37. package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts.map +1 -0
  38. package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.js +28 -0
  39. package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.js.map +1 -0
  40. package/build/ts/react/components/AppProxyLink/AppProxyLink.js +47 -0
  41. package/build/ts/react/components/AppProxyLink/AppProxyLink.js.map +1 -0
  42. package/build/ts/react/components/AppProxyLink/index.d.ts +2 -0
  43. package/build/ts/react/components/AppProxyLink/index.d.ts.map +1 -0
  44. package/build/ts/react/components/AppProxyLink/index.js +6 -0
  45. package/build/ts/react/components/AppProxyLink/index.js.map +1 -0
  46. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts +55 -0
  47. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts.map +1 -0
  48. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts +4 -0
  49. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts.map +1 -0
  50. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.js +40 -0
  51. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.js.map +1 -0
  52. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.js +65 -0
  53. package/build/ts/react/components/AppProxyProvider/AppProxyProvider.js.map +1 -0
  54. package/build/ts/react/components/AppProxyProvider/index.d.ts +2 -0
  55. package/build/ts/react/components/AppProxyProvider/index.d.ts.map +1 -0
  56. package/build/ts/react/components/AppProxyProvider/index.js +7 -0
  57. package/build/ts/react/components/AppProxyProvider/index.js.map +1 -0
  58. package/build/ts/react/components/index.d.ts +3 -0
  59. package/build/ts/react/components/index.d.ts.map +1 -1
  60. package/build/ts/react/components/index.js +3 -0
  61. package/build/ts/react/components/index.js.map +1 -1
  62. package/build/ts/server/__test-helpers/const.d.ts +1 -1
  63. package/build/ts/server/__test-helpers/test-config.d.ts +1 -1
  64. package/build/ts/server/authenticate/admin/authenticate.admin.doc.d.ts.map +1 -1
  65. package/build/ts/server/authenticate/admin/authenticate.admin.doc.js +13 -0
  66. package/build/ts/server/authenticate/admin/authenticate.admin.doc.js.map +1 -1
  67. package/build/ts/server/authenticate/admin/authenticate.js +1 -1
  68. package/build/ts/server/authenticate/admin/authenticate.js.map +1 -1
  69. package/build/ts/server/authenticate/admin/types.d.ts +27 -27
  70. package/build/ts/server/authenticate/flow/authenticate.flow.doc.d.ts.map +1 -1
  71. package/build/ts/server/authenticate/flow/authenticate.flow.doc.js +13 -0
  72. package/build/ts/server/authenticate/flow/authenticate.flow.doc.js.map +1 -1
  73. package/build/ts/server/authenticate/flow/types.d.ts +4 -4
  74. package/build/ts/server/authenticate/fulfillment-service/authenticate.d.ts +5 -0
  75. package/build/ts/server/authenticate/fulfillment-service/authenticate.d.ts.map +1 -0
  76. package/build/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts +4 -0
  77. package/build/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts.map +1 -0
  78. package/build/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.js +46 -0
  79. package/build/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.js.map +1 -0
  80. package/build/ts/server/authenticate/fulfillment-service/authenticate.js +73 -0
  81. package/build/ts/server/authenticate/fulfillment-service/authenticate.js.map +1 -0
  82. package/build/ts/server/authenticate/fulfillment-service/types.d.ts +109 -0
  83. package/build/ts/server/authenticate/fulfillment-service/types.d.ts.map +1 -0
  84. package/build/ts/server/authenticate/fulfillment-service/types.js +3 -0
  85. package/build/ts/server/authenticate/fulfillment-service/types.js.map +1 -0
  86. package/build/ts/server/authenticate/helpers/validate-session-token.d.ts +1 -1
  87. package/build/ts/server/authenticate/helpers/validate-session-token.d.ts.map +1 -1
  88. package/build/ts/server/authenticate/helpers/validate-session-token.js +12 -14
  89. package/build/ts/server/authenticate/helpers/validate-session-token.js.map +1 -1
  90. package/build/ts/server/authenticate/public/appProxy/authenticate.d.ts.map +1 -1
  91. package/build/ts/server/authenticate/public/appProxy/authenticate.js +59 -22
  92. package/build/ts/server/authenticate/public/appProxy/authenticate.js.map +1 -1
  93. package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts.map +1 -1
  94. package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.js +19 -0
  95. package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.js.map +1 -1
  96. package/build/ts/server/authenticate/public/appProxy/types.d.ts +60 -7
  97. package/build/ts/server/authenticate/public/appProxy/types.d.ts.map +1 -1
  98. package/build/ts/server/authenticate/public/checkout/authenticate.d.ts.map +1 -1
  99. package/build/ts/server/authenticate/public/checkout/authenticate.js +1 -3
  100. package/build/ts/server/authenticate/public/checkout/authenticate.js.map +1 -1
  101. package/build/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.d.ts.map +1 -1
  102. package/build/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.js +18 -0
  103. package/build/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.js.map +1 -1
  104. package/build/ts/server/authenticate/webhooks/authenticate.webhooks.doc.d.ts.map +1 -1
  105. package/build/ts/server/authenticate/webhooks/authenticate.webhooks.doc.js +13 -0
  106. package/build/ts/server/authenticate/webhooks/authenticate.webhooks.doc.js.map +1 -1
  107. package/build/ts/server/authenticate/webhooks/types.d.ts +13 -14
  108. package/build/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
  109. package/build/ts/server/boundary/index.d.ts +1 -1
  110. package/build/ts/server/boundary/index.js +1 -1
  111. package/build/ts/server/clients/admin/types.d.ts +85 -14
  112. package/build/ts/server/clients/admin/types.d.ts.map +1 -1
  113. package/build/ts/server/clients/storefront/types.d.ts +47 -0
  114. package/build/ts/server/clients/storefront/types.d.ts.map +1 -1
  115. package/build/ts/server/clients/types.d.ts +14 -1
  116. package/build/ts/server/clients/types.d.ts.map +1 -1
  117. package/build/ts/server/future/flags.d.ts +3 -1
  118. package/build/ts/server/future/flags.d.ts.map +1 -1
  119. package/build/ts/server/future/flags.js +20 -0
  120. package/build/ts/server/future/flags.js.map +1 -1
  121. package/build/ts/server/shopify-app.d.ts.map +1 -1
  122. package/build/ts/server/shopify-app.js +5 -1
  123. package/build/ts/server/shopify-app.js.map +1 -1
  124. package/build/ts/server/types.d.ts +35 -6
  125. package/build/ts/server/types.d.ts.map +1 -1
  126. package/build/ts/server/types.js.map +1 -1
  127. package/build/ts/server/version.d.ts +1 -1
  128. package/build/ts/server/version.js +1 -1
  129. package/package.json +9 -8
@@ -26,6 +26,7 @@ var englishI18n__default = /*#__PURE__*/_interopDefaultLegacy(englishI18n);
26
26
  * <caption>Set up AppProvider.</caption>
27
27
  * <description>Wrap your app in the `AppProvider` component and pass in your API key.</description>
28
28
  * ```ts
29
+ * // /app/routes/**\/*.ts
29
30
  * import {authenticate} from '~/shopify.server';
30
31
  * import {AppProvider} from '@shopify/shopify-app-remix/react';
31
32
  *
@@ -50,6 +51,7 @@ var englishI18n__default = /*#__PURE__*/_interopDefaultLegacy(englishI18n);
50
51
  * <caption>Localize Polaris components.</caption>
51
52
  * <description>Pass in a different locale for Polaris to translate its components.</description>
52
53
  * ```ts
54
+ * // /app/routes/**\/*.ts
53
55
  * import {authenticate} from '~/shopify.server';
54
56
  * import {AppProvider} from '@shopify/shopify-app-remix/react';
55
57
  *
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var react = require('@remix-run/react');
7
+ var AppProxyProvider = require('../AppProxyProvider/AppProxyProvider.js');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+
10
+ /**
11
+ * Sets up a Remix `<Form>` component that works when rendered behind an app proxy.
12
+ *
13
+ * Supports any properties accepted by the `<Form>` component.
14
+ *
15
+ * @example
16
+ * <caption>Render a form element in a proxied route.</caption>
17
+ * <description>Use an `AppProxyForm` within an `AppProxy` to create a form.</description>
18
+ * ```ts
19
+ * // /app/routes/**\/*.ts
20
+ * import {
21
+ * AppProxyProvider,
22
+ * AppProxyForm,
23
+ * } from "@shopify/shopify-app-remix/react";
24
+ * import { authenticate } from "~/shopify.server";
25
+ *
26
+ * export async function loader({ request }) {
27
+ * await authenticate.public.appProxy(request);
28
+ *
29
+ * return json({ appUrl: process.env.SHOPIFY_APP_URL });
30
+ * }
31
+ *
32
+ * export async function action({ request }) {
33
+ * await authenticate.public.appProxy(request);
34
+ *
35
+ * const formData = await request.formData();
36
+ * const field = formData.get("field")?.toString();
37
+ *
38
+ * // Perform actions
39
+ * if (field) {
40
+ * console.log("Field:", field);
41
+ * }
42
+ *
43
+ * // Return JSON to the client
44
+ * return json({ message: "Success!" });
45
+ * }
46
+ *
47
+ * export default function App() {
48
+ * const { appUrl } = useLoaderData();
49
+ * const data = useActionData();
50
+ *
51
+ * return (
52
+ * <AppProxyProvider appUrl={appUrl}>
53
+ * <AppProxyForm action="/">
54
+ * <input type="text" name="field" defaultValue={data?.field} />
55
+ *
56
+ * <input type="submit" name="Submit" />
57
+ * </AppProxyForm>
58
+ * </AppProxyProvider>
59
+ * );
60
+ * }
61
+ * ```
62
+ */
63
+ function AppProxyForm(props) {
64
+ const context = React.useContext(AppProxyProvider.AppProxyProviderContext);
65
+ if (!context) {
66
+ throw new Error('AppProxyForm must be used within an AppProxyProvider component');
67
+ }
68
+ const {
69
+ children,
70
+ action,
71
+ ...otherProps
72
+ } = props;
73
+ return /*#__PURE__*/jsxRuntime.jsx(react.Form, {
74
+ action: context.formatUrl(action, false),
75
+ ...otherProps,
76
+ children: children
77
+ });
78
+ }
79
+
80
+ exports.AppProxyForm = AppProxyForm;
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var AppProxyProvider = require('../AppProxyProvider/AppProxyProvider.js');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ /**
10
+ * Sets up an `<a />` HTML element that works when rendered behind an app proxy.
11
+ *
12
+ * Supports any properties accepted by the `<a />` HTML element.
13
+ *
14
+ * @example
15
+ * <caption>Link to a different route.</caption>
16
+ * <description>Use an `AppProxyLink` within an `AppProxyProvider` to link to a different proxied route.</description>
17
+ * ```ts
18
+ * // /app/routes/**\/*.ts
19
+ * import {authenticate} from '~/shopify.server';
20
+ * import {AppProxyProvider, AppProxyLink} from '@shopify/shopify-app-remix/react';
21
+ *
22
+ * export async function loader({ request }) {
23
+ * await authenticate.public.appProxy(request);
24
+ *
25
+ * return json({ appUrl: process.env.SHOPIFY_APP_URL });
26
+ * }
27
+ *
28
+ * export default function App() {
29
+ * const { appUrl } = useLoaderData();
30
+ *
31
+ * return (
32
+ * <AppProxyProvider appUrl={appUrl}>
33
+ * <AppProxyLink href="/other-proxy-route">Link to another route</AppProxyLink>
34
+ * </AppProxyProvider>
35
+ * );
36
+ * }
37
+ * ```
38
+ */
39
+ function AppProxyLink(props) {
40
+ const context = React.useContext(AppProxyProvider.AppProxyProviderContext);
41
+ if (!context) {
42
+ throw new Error('AppProxyLink must be used within an AppProxyProvider component');
43
+ }
44
+ const {
45
+ children,
46
+ href,
47
+ ...otherProps
48
+ } = props;
49
+ return /*#__PURE__*/jsxRuntime.jsx("a", {
50
+ href: context.formatUrl(href),
51
+ ...otherProps,
52
+ children: children
53
+ });
54
+ }
55
+
56
+ exports.AppProxyLink = AppProxyLink;
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ const AppProxyProviderContext = /*#__PURE__*/React.createContext(null);
9
+
10
+ /**
11
+ * Sets up a page to render behind a Shopify app proxy, enabling JavaScript and CSS to be loaded from the app.
12
+ *
13
+ * Also provides components that enable using other components such as links and forms within proxies.
14
+ *
15
+ * > Caution:
16
+ * Because Remix doesn't support URL rewriting, any route using this component should <b>match the pathname of the proxy
17
+ * URL exactly</b>, and <b>end in a trailing slash</b> (e.g., `https://<shop>/apps/proxy/`).
18
+ *
19
+ * @example
20
+ * <caption>Wrap a route with an AppProxyProvider component.</caption>
21
+ * <description>Wrap your route component in the `AppProxyProvider` component and pass in your app URL.</description>
22
+ * ```ts
23
+ * // /app/routes/**\/*.ts
24
+ * import {authenticate} from '~/shopify.server';
25
+ * import {AppProxyProvider} from '@shopify/shopify-app-remix/react';
26
+ *
27
+ * export async function loader({ request }) {
28
+ * await authenticate.public.appProxy(request);
29
+ *
30
+ * return json({ appUrl: process.env.SHOPIFY_APP_URL });
31
+ * }
32
+ *
33
+ * export default function App() {
34
+ * const { appUrl } = useLoaderData();
35
+ *
36
+ * return (
37
+ * <AppProxyProvider appUrl={appUrl}>
38
+ * Page content
39
+ * </AppProxyProvider>
40
+ * );
41
+ * }
42
+ * ```
43
+ */
44
+ function AppProxyProvider(props) {
45
+ const {
46
+ children,
47
+ appUrl
48
+ } = props;
49
+ const [requestUrl, setRequestUrl] = React.useState();
50
+ React.useEffect(() => setRequestUrl(new URL(window.location.href)), [setRequestUrl]);
51
+ return /*#__PURE__*/jsxRuntime.jsxs(AppProxyProviderContext.Provider, {
52
+ value: {
53
+ appUrl,
54
+ requestUrl,
55
+ formatUrl: formatProxyUrl(requestUrl)
56
+ },
57
+ children: [/*#__PURE__*/jsxRuntime.jsx("base", {
58
+ href: appUrl
59
+ }), children]
60
+ });
61
+ }
62
+ function formatProxyUrl(requestUrl) {
63
+ return (url, addOrigin = true) => {
64
+ if (!url) {
65
+ return url;
66
+ }
67
+ let finalUrl = url;
68
+ if (addOrigin && requestUrl && finalUrl.startsWith('/')) {
69
+ finalUrl = new URL(`${requestUrl.origin}${url}`).href;
70
+ }
71
+ if (!finalUrl.endsWith('/')) {
72
+ finalUrl = `${finalUrl}/`;
73
+ }
74
+ return finalUrl;
75
+ };
76
+ }
77
+
78
+ exports.AppProxyProvider = AppProxyProvider;
79
+ exports.AppProxyProviderContext = AppProxyProviderContext;
@@ -3,7 +3,14 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var AppProvider = require('./components/AppProvider/AppProvider.js');
6
+ var AppProxyProvider = require('./components/AppProxyProvider/AppProxyProvider.js');
7
+ var AppProxyForm = require('./components/AppProxyForm/AppProxyForm.js');
8
+ var AppProxyLink = require('./components/AppProxyLink/AppProxyLink.js');
6
9
 
7
10
 
8
11
 
9
12
  exports.AppProvider = AppProvider.AppProvider;
13
+ exports.AppProxyProvider = AppProxyProvider.AppProxyProvider;
14
+ exports.AppProxyProviderContext = AppProxyProvider.AppProxyProviderContext;
15
+ exports.AppProxyForm = AppProxyForm.AppProxyForm;
16
+ exports.AppProxyLink = AppProxyLink.AppProxyLink;
@@ -3,8 +3,8 @@
3
3
  var crypto = require('crypto');
4
4
  var runtime = require('@shopify/shopify-api/runtime');
5
5
  var appBridgeUrl = require('../../authenticate/helpers/app-bridge-url.js');
6
- require('isbot');
7
6
  require('@remix-run/server-runtime');
7
+ require('isbot');
8
8
 
9
9
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
10
 
@@ -138,11 +138,7 @@ async function getSessionTokenContext(params, request) {
138
138
  })
139
139
  });
140
140
  if (config.isEmbeddedApp) {
141
- const payload = await validateSessionToken.validateSessionToken({
142
- config,
143
- logger,
144
- api
145
- }, sessionToken);
141
+ const payload = await validateSessionToken.validateSessionToken(params, request, sessionToken);
146
142
  const dest = new URL(payload.dest);
147
143
  const shop = dest.hostname;
148
144
  logger.debug('Session token is valid', {
@@ -3,8 +3,8 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var shopifyApi = require('@shopify/shopify-api');
6
- require('isbot');
7
6
  var respondToInvalidSessionToken = require('../../helpers/respond-to-invalid-session-token.js');
7
+ require('isbot');
8
8
  require('@remix-run/server-runtime');
9
9
  var handleClientError = require('../helpers/handle-client-error.js');
10
10
  var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var shopifyApi = require('@shopify/shopify-api');
6
+ var factory = require('../../clients/admin/factory.js');
7
+
8
+ function authenticateFulfillmentServiceFactory(params) {
9
+ const {
10
+ api,
11
+ config,
12
+ logger
13
+ } = params;
14
+ return async function authenticate(request) {
15
+ logger.info('Authenticating fulfillment service request');
16
+ if (request.method !== 'POST') {
17
+ logger.debug('Received a non-POST request for fulfillment service. Only POST requests are allowed.', {
18
+ url: request.url,
19
+ method: request.method
20
+ });
21
+ throw new Response(undefined, {
22
+ status: 405,
23
+ statusText: 'Method not allowed'
24
+ });
25
+ }
26
+ const rawBody = await request.text();
27
+ const result = await api.fulfillmentService.validate({
28
+ rawBody,
29
+ rawRequest: request
30
+ });
31
+ if (!result.valid) {
32
+ logger.error('Received an invalid fulfillment service request', {
33
+ reason: result.reason
34
+ });
35
+ throw new Response(undefined, {
36
+ status: 400,
37
+ statusText: 'Bad Request'
38
+ });
39
+ }
40
+ const payload = JSON.parse(rawBody);
41
+ const shop = request.headers.get(shopifyApi.ShopifyHeader.Domain) || '';
42
+ logger.debug('Fulfillment service request is valid, looking for an offline session', {
43
+ shop
44
+ });
45
+ const sessionId = api.session.getOfflineId(shop);
46
+ const session = await config.sessionStorage.loadSession(sessionId);
47
+ if (!session) {
48
+ logger.info('Fulfillment service request could not find session', {
49
+ shop
50
+ });
51
+ throw new Response(undefined, {
52
+ status: 400,
53
+ statusText: 'Bad Request'
54
+ });
55
+ }
56
+ logger.debug('Found a session for the fulfillment service request', {
57
+ shop
58
+ });
59
+ return {
60
+ session,
61
+ payload,
62
+ admin: factory.adminClientFactory({
63
+ params,
64
+ session
65
+ })
66
+ };
67
+ };
68
+ }
69
+
70
+ exports.authenticateFulfillmentServiceFactory = authenticateFulfillmentServiceFactory;
@@ -2,12 +2,15 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- async function validateSessionToken({
6
- api,
7
- logger
8
- }, token, {
5
+ var respondToInvalidSessionToken = require('./respond-to-invalid-session-token.js');
6
+
7
+ async function validateSessionToken(params, request, token, {
9
8
  checkAudience = true
10
9
  } = {}) {
10
+ const {
11
+ api,
12
+ logger
13
+ } = params;
11
14
  logger.debug('Validating session token');
12
15
  try {
13
16
  const payload = await api.session.decodeSessionToken(token, {
@@ -19,9 +22,10 @@ async function validateSessionToken({
19
22
  return payload;
20
23
  } catch (error) {
21
24
  logger.debug(`Failed to validate session token: ${error.message}`);
22
- throw new Response(undefined, {
23
- status: 401,
24
- statusText: 'Unauthorized'
25
+ throw respondToInvalidSessionToken.respondToInvalidSessionToken({
26
+ params,
27
+ request,
28
+ retryRequest: true
25
29
  });
26
30
  }
27
31
  }
@@ -13,30 +13,15 @@ function authenticateAppProxyFactory(params) {
13
13
  } = params;
14
14
  return async function authenticate(request) {
15
15
  logger.info('Authenticating app proxy request');
16
- const {
17
- searchParams
18
- } = new URL(request.url);
19
- const query = Object.fromEntries(searchParams.entries());
20
- let isValid = false;
21
- try {
22
- isValid = await api.utils.validateHmac(query, {
23
- signator: 'appProxy'
24
- });
25
- } catch (error) {
26
- logger.info(error.message);
27
- throw new Response(undefined, {
28
- status: 400,
29
- statusText: 'Bad Request'
30
- });
31
- }
32
- if (!isValid) {
16
+ const url = new URL(request.url);
17
+ if (!(await validateAppProxyHmac(params, url))) {
33
18
  logger.info('App proxy request has invalid signature');
34
19
  throw new Response(undefined, {
35
20
  status: 400,
36
21
  statusText: 'Bad Request'
37
22
  });
38
23
  }
39
- const shop = searchParams.get('shop');
24
+ const shop = url.searchParams.get('shop');
40
25
  const sessionId = api.session.getOfflineId(shop);
41
26
  const session = await config.sessionStorage.loadSession(sessionId);
42
27
  if (!session) {
@@ -64,8 +49,9 @@ function authenticateAppProxyFactory(params) {
64
49
  };
65
50
  }
66
51
  const liquid = (body, initAndOptions) => {
52
+ const processedBody = processLiquidBody(body);
67
53
  if (typeof initAndOptions !== 'object') {
68
- return new Response(body, {
54
+ return new Response(processedBody, {
69
55
  status: initAndOptions || 200,
70
56
  headers: {
71
57
  'Content-Type': 'application/liquid'
@@ -76,7 +62,7 @@ const liquid = (body, initAndOptions) => {
76
62
  layout,
77
63
  ...responseInit
78
64
  } = initAndOptions || {};
79
- const responseBody = layout === false ? `{% layout none %} ${body}` : body;
65
+ const responseBody = layout === false ? `{% layout none %} ${processedBody}` : processedBody;
80
66
  const headers = new Headers(responseInit.headers);
81
67
  headers.set('Content-Type', 'application/liquid');
82
68
  return new Response(responseBody, {
@@ -84,5 +70,48 @@ const liquid = (body, initAndOptions) => {
84
70
  headers
85
71
  });
86
72
  };
73
+ async function validateAppProxyHmac(params, url) {
74
+ const {
75
+ api,
76
+ logger
77
+ } = params;
78
+ try {
79
+ let searchParams = new URLSearchParams(url.search);
80
+ if (!searchParams.get('index')) {
81
+ searchParams.delete('index');
82
+ }
83
+ let isValid = await api.utils.validateHmac(Object.fromEntries(searchParams.entries()), {
84
+ signator: 'appProxy'
85
+ });
86
+ if (!isValid) {
87
+ const cleanPath = url.pathname.replace(/^\//, '').replace(/\/$/, '').replaceAll('/', '.');
88
+ const data = `routes%2F${cleanPath}`;
89
+ searchParams = new URLSearchParams(`?_data=${data}&${searchParams.toString().replace(/^\?/, '')}`);
90
+ isValid = await api.utils.validateHmac(Object.fromEntries(searchParams.entries()), {
91
+ signator: 'appProxy'
92
+ });
93
+ if (!isValid) {
94
+ const searchParams = new URLSearchParams(`?_data=${data}._index&${url.search.replace(/^\?/, '')}`);
95
+ isValid = await api.utils.validateHmac(Object.fromEntries(searchParams.entries()), {
96
+ signator: 'appProxy'
97
+ });
98
+ }
99
+ }
100
+ return isValid;
101
+ } catch (error) {
102
+ logger.info(error.message);
103
+ throw new Response(undefined, {
104
+ status: 400,
105
+ statusText: 'Bad Request'
106
+ });
107
+ }
108
+ }
109
+ function processLiquidBody(body) {
110
+ return body
111
+ // Add trailing slashes to relative form action URLs
112
+ .replaceAll(/<(form[^>]+)action="(\/[^"?]+)(\?[^"]+)?">/g, '<$1action="$2/$3">')
113
+ // Add trailing slashes to relative link href URLs
114
+ .replaceAll(/<(a[^>]+)href="(\/[^"?]+)(\?[^"]+)?">/g, '<$1href="$2/$3">');
115
+ }
87
116
 
88
117
  exports.authenticateAppProxyFactory = authenticateAppProxyFactory;
@@ -28,7 +28,7 @@ function authenticateCheckoutFactory(params) {
28
28
  });
29
29
  }
30
30
  return {
31
- sessionToken: await validateSessionToken.validateSessionToken(params, sessionTokenHeader, {
31
+ sessionToken: await validateSessionToken.validateSessionToken(params, request, sessionTokenHeader, {
32
32
  checkAudience: false
33
33
  }),
34
34
  cors: ensureCorsHeaders.ensureCORSHeadersFactory(params, request, corsHeaders)
@@ -25,7 +25,7 @@ const boundary = {
25
25
  */
26
26
  error: error.errorBoundary,
27
27
  /**
28
- * A function that sets the appropriate document repsonse headers.
28
+ * A function that sets the appropriate document response headers.
29
29
  *
30
30
  * @example
31
31
  * <caption>Catching errors in a route</caption>
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // When adding new flags, you should also add them to the `TEST_FUTURE_FLAGS` object in `test-config.ts` to ensure that
6
+
7
+ // it doesn't cause regressions.
8
+
9
+ // When adding new flags, use this format:
10
+ // vX_myFutureFlag: Future extends FutureFlags ? Future['vX_myFutureFlag'] : false;
11
+
12
+ function logDisabledFutureFlags(config, logger) {
13
+ const logFlag = (flag, message) => logger.info(`Future flag ${flag} is disabled.\n\n ${message}\n`);
14
+ if (!config.future.v3_authenticatePublic) {
15
+ logFlag('v3_authenticatePublic', 'Enable this flag to allow appProxy and checkout in `shopify.authenticate.public`.');
16
+ }
17
+ if (!config.future.v3_lineItemBilling) {
18
+ logFlag('v3_lineItemBilling', 'Enable this flag to allow billing plans with multiple line items.');
19
+ }
20
+ if (!config.future.v3_webhookAdminContext) {
21
+ logFlag('v3_webhookAdminContext', 'Enable this flag to use the standard Admin context when calling `shopify.authenticate.webhook`.');
22
+ }
23
+ if (!config.future.unstable_newEmbeddedAuthStrategy) {
24
+ logFlag('unstable_newEmbeddedAuthStrategy', 'Enable this to use OAuth token exchange instead of auth code to generate API access tokens.' + '\n Your app must be using Shopify managed install: https://shopify.dev/docs/apps/auth/installation');
25
+ }
26
+ }
27
+
28
+ exports.logDisabledFutureFlags = logDisabledFutureFlags;
@@ -8,11 +8,11 @@ var types = require('./types.js');
8
8
  var version = require('./version.js');
9
9
  var register = require('./authenticate/webhooks/register.js');
10
10
  var authenticate = require('./authenticate/admin/authenticate.js');
11
- var authenticate$2 = require('./authenticate/webhooks/authenticate.js');
11
+ var authenticate$3 = require('./authenticate/webhooks/authenticate.js');
12
12
  var overrideLogger = require('./override-logger.js');
13
13
  var addResponseHeaders = require('./authenticate/helpers/add-response-headers.js');
14
- require('isbot');
15
14
  require('@remix-run/server-runtime');
15
+ require('isbot');
16
16
  var login = require('./authenticate/login/login.js');
17
17
  var factory$1 = require('./unauthenticated/admin/factory.js');
18
18
  var factory = require('./authenticate/public/factory.js');
@@ -21,6 +21,8 @@ var authCodeFlow = require('./authenticate/admin/strategies/auth-code-flow.js');
21
21
  var tokenExchange = require('./authenticate/admin/strategies/token-exchange.js');
22
22
  var idempotentPromiseHandler = require('./authenticate/helpers/idempotent-promise-handler.js');
23
23
  var authenticate$1 = require('./authenticate/flow/authenticate.js');
24
+ var authenticate$2 = require('./authenticate/fulfillment-service/authenticate.js');
25
+ var flags = require('./future/flags.js');
24
26
 
25
27
  /**
26
28
  * Creates an object your app will use to interact with Shopify.
@@ -67,7 +69,8 @@ function shopifyApp(appConfig) {
67
69
  admin: authStrategy,
68
70
  flow: authenticate$1.authenticateFlowFactory(params),
69
71
  public: factory.authenticatePublicFactory(params),
70
- webhook: authenticate$2.authenticateWebhookFactory(params)
72
+ fulfillmentService: authenticate$2.authenticateFulfillmentServiceFactory(params),
73
+ webhook: authenticate$3.authenticateWebhookFactory(params)
71
74
  },
72
75
  unauthenticated: {
73
76
  admin: factory$1.unauthenticatedAdminContextFactory(params),
@@ -77,6 +80,7 @@ function shopifyApp(appConfig) {
77
80
  if (isAppStoreApp(shopify, appConfig) || isSingleMerchantApp(shopify, appConfig)) {
78
81
  shopify.login = login.loginFactory(params);
79
82
  }
83
+ flags.logDisabledFutureFlags(config, logger);
80
84
  return shopify;
81
85
  }
82
86
  function isAppStoreApp(_shopify, config) {
@@ -114,7 +118,8 @@ function deriveApi(appConfig) {
114
118
  isCustomStoreApp: appConfig.distribution === types.AppDistribution.ShopifyAdmin,
115
119
  future: {
116
120
  v10_lineItemBilling: (_appConfig$future = appConfig.future) === null || _appConfig$future === void 0 ? void 0 : _appConfig$future.v3_lineItemBilling
117
- }
121
+ },
122
+ _logDisabledFutureFlags: false
118
123
  });
119
124
  }
120
125
  function deriveConfig(appConfig, apiConfig) {
@@ -10,10 +10,6 @@ let AppDistribution = /*#__PURE__*/function (AppDistribution) {
10
10
  AppDistribution["ShopifyAdmin"] = "shopify_admin";
11
11
  return AppDistribution;
12
12
  }({});
13
-
14
- // This can't be a type because it would reference itself
15
- // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
16
-
17
13
  let LoginErrorType = /*#__PURE__*/function (LoginErrorType) {
18
14
  LoginErrorType["MissingShop"] = "MISSING_SHOP";
19
15
  LoginErrorType["InvalidShop"] = "INVALID_SHOP";
@@ -2,6 +2,6 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- const SHOPIFY_REMIX_LIBRARY_VERSION = '2.6.1';
5
+ const SHOPIFY_REMIX_LIBRARY_VERSION = '2.8.0';
6
6
 
7
7
  exports.SHOPIFY_REMIX_LIBRARY_VERSION = SHOPIFY_REMIX_LIBRARY_VERSION;
@@ -39,6 +39,7 @@ export interface AppProviderProps extends Omit<PolarisAppProviderProps, 'linkCom
39
39
  * <caption>Set up AppProvider.</caption>
40
40
  * <description>Wrap your app in the `AppProvider` component and pass in your API key.</description>
41
41
  * ```ts
42
+ * // /app/routes/**\/*.ts
42
43
  * import {authenticate} from '~/shopify.server';
43
44
  * import {AppProvider} from '@shopify/shopify-app-remix/react';
44
45
  *
@@ -63,6 +64,7 @@ export interface AppProviderProps extends Omit<PolarisAppProviderProps, 'linkCom
63
64
  * <caption>Localize Polaris components.</caption>
64
65
  * <description>Pass in a different locale for Polaris to translate its components.</description>
65
66
  * ```ts
67
+ * // /app/routes/**\/*.ts
66
68
  * import {authenticate} from '~/shopify.server';
67
69
  * import {AppProvider} from '@shopify/shopify-app-remix/react';
68
70
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AppProvider.d.ts","sourceRoot":"","sources":["../../../../../src/react/components/AppProvider/AppProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,gBAAgB,IAAI,uBAAuB,EAC5C,MAAM,kBAAkB,CAAC;AAM1B,MAAM,WAAW,gBACf,SAAQ,IAAI,CAAC,uBAAuB,EAAE,eAAe,GAAG,MAAM,CAAC;IAC/D;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,IAAI,CAAC,EAAE,uBAAuB,CAAC,MAAM,CAAC,CAAC;IACvC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAsBlD"}
1
+ {"version":3,"file":"AppProvider.d.ts","sourceRoot":"","sources":["../../../../../src/react/components/AppProvider/AppProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,gBAAgB,IAAI,uBAAuB,EAC5C,MAAM,kBAAkB,CAAC;AAM1B,MAAM,WAAW,gBACf,SAAQ,IAAI,CAAC,uBAAuB,EAAE,eAAe,GAAG,MAAM,CAAC;IAC/D;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,IAAI,CAAC,EAAE,uBAAuB,CAAC,MAAM,CAAC,CAAC;IACvC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAsBlD"}
@@ -21,6 +21,7 @@ var RemixPolarisLink_1 = require("../RemixPolarisLink");
21
21
  * <caption>Set up AppProvider.</caption>
22
22
  * <description>Wrap your app in the `AppProvider` component and pass in your API key.</description>
23
23
  * ```ts
24
+ * // /app/routes/**\/*.ts
24
25
  * import {authenticate} from '~/shopify.server';
25
26
  * import {AppProvider} from '@shopify/shopify-app-remix/react';
26
27
  *
@@ -45,6 +46,7 @@ var RemixPolarisLink_1 = require("../RemixPolarisLink");
45
46
  * <caption>Localize Polaris components.</caption>
46
47
  * <description>Pass in a different locale for Polaris to translate its components.</description>
47
48
  * ```ts
49
+ * // /app/routes/**\/*.ts
48
50
  * import {authenticate} from '~/shopify.server';
49
51
  * import {AppProvider} from '@shopify/shopify-app-remix/react';
50
52
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AppProvider.js","sourceRoot":"","sources":["../../../../../src/react/components/AppProvider/AppProvider.tsx"],"names":[],"mappings":";;;;;AAAA,4CAG0B;AAC1B,qFAA2D;AAE3D,qCAA2C;AAC3C,wDAAqD;AA8BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,SAAgB,WAAW,CAAC,KAAuB;IAE/C,IAAA,QAAQ,GAMN,KAAK,SANC,EACR,MAAM,GAKJ,KAAK,OALD,EACN,IAAI,GAIF,KAAK,KAJH,EACJ,KAGE,KAAK,cAHa,EAApB,aAAa,mBAAG,IAAI,KAAA,EACpB,KAEE,KAAK,iBAF0B,EAAjC,gBAAgB,mBAAG,sBAAc,KAAA,EAC9B,YAAY,kBACb,KAAK,EAPH,mEAOL,CADgB,CACP;IAEV,OAAO,CACL,6DACG,aAAa,IAAI,mCAAQ,GAAG,EAAE,gBAAgB,kBAAgB,MAAM,GAAI,EACzE,uBAAC,qBAAkB,uBACb,YAAY,IAChB,aAAa,EAAE,mCAAgB,EAC/B,IAAI,EAAE,IAAI,IAAI,iBAAW,YAExB,QAAQ,IACU,IACpB,CACJ,CAAC;AACJ,CAAC;AAtBD,kCAsBC"}
1
+ {"version":3,"file":"AppProvider.js","sourceRoot":"","sources":["../../../../../src/react/components/AppProvider/AppProvider.tsx"],"names":[],"mappings":";;;;;AAAA,4CAG0B;AAC1B,qFAA2D;AAE3D,qCAA2C;AAC3C,wDAAqD;AA8BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,SAAgB,WAAW,CAAC,KAAuB;IAE/C,IAAA,QAAQ,GAMN,KAAK,SANC,EACR,MAAM,GAKJ,KAAK,OALD,EACN,IAAI,GAIF,KAAK,KAJH,EACJ,KAGE,KAAK,cAHa,EAApB,aAAa,mBAAG,IAAI,KAAA,EACpB,KAEE,KAAK,iBAF0B,EAAjC,gBAAgB,mBAAG,sBAAc,KAAA,EAC9B,YAAY,kBACb,KAAK,EAPH,mEAOL,CADgB,CACP;IAEV,OAAO,CACL,6DACG,aAAa,IAAI,mCAAQ,GAAG,EAAE,gBAAgB,kBAAgB,MAAM,GAAI,EACzE,uBAAC,qBAAkB,uBACb,YAAY,IAChB,aAAa,EAAE,mCAAgB,EAC/B,IAAI,EAAE,IAAI,IAAI,iBAAW,YAExB,QAAQ,IACU,IACpB,CACJ,CAAC;AACJ,CAAC;AAtBD,kCAsBC"}