@shopify/shopify-app-remix 2.7.0 → 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.
- package/build/cjs/react/components/AppProvider/AppProvider.js +2 -0
- package/build/cjs/react/components/AppProxyForm/AppProxyForm.js +80 -0
- package/build/cjs/react/components/AppProxyLink/AppProxyLink.js +56 -0
- package/build/cjs/react/components/AppProxyProvider/AppProxyProvider.js +79 -0
- package/build/cjs/react/index.js +7 -0
- package/build/cjs/server/adapters/node/index.js +1 -1
- package/build/cjs/server/authenticate/admin/authenticate.js +1 -5
- package/build/cjs/server/authenticate/admin/strategies/token-exchange.js +1 -1
- package/build/cjs/server/authenticate/helpers/validate-session-token.js +11 -7
- package/build/cjs/server/authenticate/public/appProxy/authenticate.js +49 -20
- package/build/cjs/server/authenticate/public/checkout/authenticate.js +1 -1
- package/build/cjs/server/boundary/index.js +1 -1
- package/build/cjs/server/shopify-app.js +1 -1
- package/build/cjs/server/version.js +1 -1
- package/build/ts/react/components/AppProvider/AppProvider.d.ts +2 -0
- package/build/ts/react/components/AppProvider/AppProvider.d.ts.map +1 -1
- package/build/ts/react/components/AppProvider/AppProvider.js +2 -0
- package/build/ts/react/components/AppProvider/AppProvider.js.map +1 -1
- package/build/ts/react/components/AppProxyForm/AppProxyForm.d.ts +59 -0
- package/build/ts/react/components/AppProxyForm/AppProxyForm.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts +4 -0
- package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.js +28 -0
- package/build/ts/react/components/AppProxyForm/AppProxyForm.doc.js.map +1 -0
- package/build/ts/react/components/AppProxyForm/AppProxyForm.js +71 -0
- package/build/ts/react/components/AppProxyForm/AppProxyForm.js.map +1 -0
- package/build/ts/react/components/AppProxyForm/index.d.ts +2 -0
- package/build/ts/react/components/AppProxyForm/index.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyForm/index.js +6 -0
- package/build/ts/react/components/AppProxyForm/index.js.map +1 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.d.ts +36 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts +4 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.js +28 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.doc.js.map +1 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.js +47 -0
- package/build/ts/react/components/AppProxyLink/AppProxyLink.js.map +1 -0
- package/build/ts/react/components/AppProxyLink/index.d.ts +2 -0
- package/build/ts/react/components/AppProxyLink/index.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyLink/index.js +6 -0
- package/build/ts/react/components/AppProxyLink/index.js.map +1 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts +55 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts +4 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.js +40 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.doc.js.map +1 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.js +65 -0
- package/build/ts/react/components/AppProxyProvider/AppProxyProvider.js.map +1 -0
- package/build/ts/react/components/AppProxyProvider/index.d.ts +2 -0
- package/build/ts/react/components/AppProxyProvider/index.d.ts.map +1 -0
- package/build/ts/react/components/AppProxyProvider/index.js +7 -0
- package/build/ts/react/components/AppProxyProvider/index.js.map +1 -0
- package/build/ts/react/components/index.d.ts +3 -0
- package/build/ts/react/components/index.d.ts.map +1 -1
- package/build/ts/react/components/index.js +3 -0
- package/build/ts/react/components/index.js.map +1 -1
- package/build/ts/server/__test-helpers/const.d.ts +1 -1
- package/build/ts/server/__test-helpers/test-config.d.ts +1 -1
- package/build/ts/server/authenticate/admin/authenticate.js +1 -1
- package/build/ts/server/authenticate/admin/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/helpers/validate-session-token.d.ts +1 -1
- package/build/ts/server/authenticate/helpers/validate-session-token.d.ts.map +1 -1
- package/build/ts/server/authenticate/helpers/validate-session-token.js +12 -14
- package/build/ts/server/authenticate/helpers/validate-session-token.js.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/authenticate.d.ts.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/authenticate.js +59 -22
- package/build/ts/server/authenticate/public/appProxy/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/types.d.ts +37 -2
- package/build/ts/server/authenticate/public/appProxy/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/public/checkout/authenticate.d.ts.map +1 -1
- package/build/ts/server/authenticate/public/checkout/authenticate.js +1 -3
- package/build/ts/server/authenticate/public/checkout/authenticate.js.map +1 -1
- package/build/ts/server/boundary/index.d.ts +1 -1
- package/build/ts/server/boundary/index.js +1 -1
- package/build/ts/server/version.d.ts +1 -1
- package/build/ts/server/version.js +1 -1
- package/package.json +8 -7
|
@@ -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;
|
package/build/cjs/react/index.js
CHANGED
|
@@ -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');
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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(
|
|
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 %} ${
|
|
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
|
|
28
|
+
* A function that sets the appropriate document response headers.
|
|
29
29
|
*
|
|
30
30
|
* @example
|
|
31
31
|
* <caption>Catching errors in a route</caption>
|
|
@@ -11,8 +11,8 @@ var authenticate = require('./authenticate/admin/authenticate.js');
|
|
|
11
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');
|
|
@@ -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
|
|
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
|
|
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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type FormProps } from '@remix-run/react';
|
|
2
|
+
export interface AppProxyFormProps extends FormProps {
|
|
3
|
+
action: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Sets up a Remix `<Form>` component that works when rendered behind an app proxy.
|
|
7
|
+
*
|
|
8
|
+
* Supports any properties accepted by the `<Form>` component.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <caption>Render a form element in a proxied route.</caption>
|
|
12
|
+
* <description>Use an `AppProxyForm` within an `AppProxy` to create a form.</description>
|
|
13
|
+
* ```ts
|
|
14
|
+
* // /app/routes/**\/*.ts
|
|
15
|
+
* import {
|
|
16
|
+
* AppProxyProvider,
|
|
17
|
+
* AppProxyForm,
|
|
18
|
+
* } from "@shopify/shopify-app-remix/react";
|
|
19
|
+
* import { authenticate } from "~/shopify.server";
|
|
20
|
+
*
|
|
21
|
+
* export async function loader({ request }) {
|
|
22
|
+
* await authenticate.public.appProxy(request);
|
|
23
|
+
*
|
|
24
|
+
* return json({ appUrl: process.env.SHOPIFY_APP_URL });
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* export async function action({ request }) {
|
|
28
|
+
* await authenticate.public.appProxy(request);
|
|
29
|
+
*
|
|
30
|
+
* const formData = await request.formData();
|
|
31
|
+
* const field = formData.get("field")?.toString();
|
|
32
|
+
*
|
|
33
|
+
* // Perform actions
|
|
34
|
+
* if (field) {
|
|
35
|
+
* console.log("Field:", field);
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* // Return JSON to the client
|
|
39
|
+
* return json({ message: "Success!" });
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* export default function App() {
|
|
43
|
+
* const { appUrl } = useLoaderData();
|
|
44
|
+
* const data = useActionData();
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <AppProxyProvider appUrl={appUrl}>
|
|
48
|
+
* <AppProxyForm action="/">
|
|
49
|
+
* <input type="text" name="field" defaultValue={data?.field} />
|
|
50
|
+
*
|
|
51
|
+
* <input type="submit" name="Submit" />
|
|
52
|
+
* </AppProxyForm>
|
|
53
|
+
* </AppProxyProvider>
|
|
54
|
+
* );
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function AppProxyForm(props: AppProxyFormProps): import("react/jsx-runtime").JSX.Element;
|
|
59
|
+
//# sourceMappingURL=AppProxyForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppProxyForm.d.ts","sourceRoot":"","sources":["../../../../../src/react/components/AppProxyForm/AppProxyForm.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAItD,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IAClD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,2CAgBpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppProxyForm.doc.d.ts","sourceRoot":"","sources":["../../../../../src/react/components/AppProxyForm/AppProxyForm.doc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,6BAA6B,EAAC,MAAM,wBAAwB,CAAC;AAErE,QAAA,MAAM,IAAI,EAAE,6BAuBX,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var data = {
|
|
4
|
+
name: 'AppProxyForm',
|
|
5
|
+
descriptionType: 'AppProxyFormGeneratedType',
|
|
6
|
+
description: '',
|
|
7
|
+
category: 'App proxy components',
|
|
8
|
+
type: 'component',
|
|
9
|
+
isVisualComponent: false,
|
|
10
|
+
definitions: [],
|
|
11
|
+
jsDocTypeExamples: ['AppProxyFormGeneratedType'],
|
|
12
|
+
related: [
|
|
13
|
+
{
|
|
14
|
+
name: 'authenticate.public.appProxy',
|
|
15
|
+
subtitle: 'Authenticating app proxy requests.',
|
|
16
|
+
url: '/docs/api/shopify-app-remix/authenticate/public/app-proxy',
|
|
17
|
+
type: 'remix',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'AppProxyProvider',
|
|
21
|
+
subtitle: 'Enable JavaScript in pages loaded through app proxies.',
|
|
22
|
+
url: '/docs/api/shopify-app-remix/app-proxy-components/appproxyprovider',
|
|
23
|
+
type: 'remix',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
exports.default = data;
|
|
28
|
+
//# sourceMappingURL=AppProxyForm.doc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppProxyForm.doc.js","sourceRoot":"","sources":["../../../../../src/react/components/AppProxyForm/AppProxyForm.doc.ts"],"names":[],"mappings":";;AAEA,IAAM,IAAI,GAAkC;IAC1C,IAAI,EAAE,cAAc;IACpB,eAAe,EAAE,2BAA2B;IAC5C,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,sBAAsB;IAChC,IAAI,EAAE,WAAW;IACjB,iBAAiB,EAAE,KAAK;IACxB,WAAW,EAAE,EAAE;IACf,iBAAiB,EAAE,CAAC,2BAA2B,CAAC;IAChD,OAAO,EAAE;QACP;YACE,IAAI,EAAE,8BAA8B;YACpC,QAAQ,EAAE,oCAAoC;YAC9C,GAAG,EAAE,2DAA2D;YAChE,IAAI,EAAE,OAAO;SACd;QACD;YACE,IAAI,EAAE,kBAAkB;YACxB,QAAQ,EAAE,wDAAwD;YAClE,GAAG,EAAE,mEAAmE;YACxE,IAAI,EAAE,OAAO;SACd;KACF;CACF,CAAC;AAEF,kBAAe,IAAI,CAAC"}
|