@shopify/shopify-app-remix 4.1.1 → 4.2.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.
- package/dist/cjs/react/components/AppProvider/AppProvider.js.map +1 -1
- package/dist/cjs/react/components/AppProxyForm/AppProxyForm.js +4 -3
- package/dist/cjs/react/components/AppProxyForm/AppProxyForm.js.map +1 -1
- package/dist/cjs/react/components/AppProxyLink/AppProxyLink.js +4 -3
- package/dist/cjs/react/components/AppProxyLink/AppProxyLink.js.map +1 -1
- package/dist/cjs/react/components/AppProxyProvider/AppProxyProvider.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/helpers/redirect.js +2 -2
- package/dist/cjs/server/authenticate/admin/helpers/redirect.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/strategies/merchant-custom-app.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/strategies/token-exchange.js.map +1 -1
- package/dist/cjs/server/authenticate/webhooks/authenticate.js +34 -10
- package/dist/cjs/server/authenticate/webhooks/authenticate.js.map +1 -1
- package/dist/cjs/server/future/flags.js.map +1 -1
- package/dist/cjs/server/shopify-app.js +1 -0
- package/dist/cjs/server/shopify-app.js.map +1 -1
- package/dist/cjs/server/version.js +1 -1
- package/dist/cjs/server/version.js.map +1 -1
- package/dist/esm/react/components/AppProvider/AppProvider.mjs.map +1 -1
- package/dist/esm/react/components/AppProxyForm/AppProxyForm.mjs +5 -4
- package/dist/esm/react/components/AppProxyForm/AppProxyForm.mjs.map +1 -1
- package/dist/esm/react/components/AppProxyLink/AppProxyLink.mjs +5 -4
- package/dist/esm/react/components/AppProxyLink/AppProxyLink.mjs.map +1 -1
- package/dist/esm/react/components/AppProxyProvider/AppProxyProvider.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/helpers/redirect.mjs +2 -2
- package/dist/esm/server/authenticate/admin/helpers/redirect.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/strategies/auth-code-flow.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/strategies/merchant-custom-app.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/strategies/token-exchange.mjs.map +1 -1
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs +35 -11
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs.map +1 -1
- package/dist/esm/server/future/flags.mjs.map +1 -1
- package/dist/esm/server/shopify-app.mjs +1 -0
- package/dist/esm/server/shopify-app.mjs.map +1 -1
- package/dist/esm/server/version.mjs +1 -1
- package/dist/esm/server/version.mjs.map +1 -1
- package/dist/ts/react/components/AppProvider/AppProvider.d.ts +5 -1
- package/dist/ts/react/components/AppProvider/AppProvider.d.ts.map +1 -1
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.d.ts +2 -1
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.d.ts.map +1 -1
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.d.ts +2 -1
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.d.ts.map +1 -1
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts +5 -1
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/billing/types.d.ts +4 -0
- package/dist/ts/server/authenticate/admin/billing/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/scope/types.d.ts +2 -1
- package/dist/ts/server/authenticate/admin/scope/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/strategies/merchant-custom-app.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/strategies/token-exchange.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/types.d.ts +10 -0
- package/dist/ts/server/authenticate/admin/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/flow/types.d.ts +4 -0
- package/dist/ts/server/authenticate/flow/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/fulfillment-service/types.d.ts +4 -0
- package/dist/ts/server/authenticate/fulfillment-service/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/appProxy/types.d.ts +4 -0
- package/dist/ts/server/authenticate/public/appProxy/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/checkout/types.d.ts +4 -0
- package/dist/ts/server/authenticate/public/checkout/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/customer-account/types.d.ts +4 -0
- package/dist/ts/server/authenticate/public/customer-account/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/extension/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/webhooks/authenticate.d.ts.map +1 -1
- package/dist/ts/server/authenticate/webhooks/types.d.ts +36 -3
- package/dist/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
- package/dist/ts/server/boundary/error.d.ts +1 -1
- package/dist/ts/server/boundary/error.d.ts.map +1 -1
- package/dist/ts/server/clients/admin/types.d.ts +4 -0
- package/dist/ts/server/clients/admin/types.d.ts.map +1 -1
- package/dist/ts/server/clients/storefront/types.d.ts +4 -0
- package/dist/ts/server/clients/storefront/types.d.ts.map +1 -1
- package/dist/ts/server/config-types.d.ts.map +1 -1
- package/dist/ts/server/future/flags.d.ts +6 -0
- package/dist/ts/server/future/flags.d.ts.map +1 -1
- package/dist/ts/server/shopify-app.d.ts +1 -0
- package/dist/ts/server/shopify-app.d.ts.map +1 -1
- package/dist/ts/server/unauthenticated/admin/types.d.ts +4 -0
- package/dist/ts/server/unauthenticated/admin/types.d.ts.map +1 -1
- package/dist/ts/server/unauthenticated/storefront/types.d.ts +4 -0
- package/dist/ts/server/unauthenticated/storefront/types.d.ts.map +1 -1
- package/dist/ts/server/version.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +19 -15
- package/src/react/components/AppProvider/AppProvider.tsx +151 -0
- package/src/react/components/AppProvider/index.ts +1 -0
- package/src/react/components/AppProxyForm/AppProxyForm.tsx +85 -0
- package/src/react/components/AppProxyForm/index.ts +1 -0
- package/src/react/components/AppProxyLink/AppProxyLink.tsx +61 -0
- package/src/react/components/AppProxyLink/index.ts +1 -0
- package/src/react/components/AppProxyProvider/AppProxyProvider.tsx +104 -0
- package/src/react/components/AppProxyProvider/index.ts +5 -0
- package/src/react/components/RemixPolarisLink.tsx +14 -0
- package/src/react/components/index.ts +4 -0
- package/src/react/const.ts +2 -0
- package/src/react/index.ts +1 -0
- package/src/server/__test-helpers/const.ts +14 -0
- package/src/server/__test-helpers/expect-admin-api-client.ts +51 -0
- package/src/server/__test-helpers/expect-begin-auth-redirect.ts +22 -0
- package/src/server/__test-helpers/expect-document-request-headers.ts +25 -0
- package/src/server/__test-helpers/expect-exit-iframe.ts +37 -0
- package/src/server/__test-helpers/expect-login-redirect.ts +8 -0
- package/src/server/__test-helpers/expect-storefront-api-client.ts +49 -0
- package/src/server/__test-helpers/expect-token-refresh.ts +188 -0
- package/src/server/__test-helpers/get-hmac.ts +7 -0
- package/src/server/__test-helpers/get-jwt.ts +10 -0
- package/src/server/__test-helpers/get-thrown-error.ts +19 -0
- package/src/server/__test-helpers/get-thrown-response.ts +17 -0
- package/src/server/__test-helpers/index.ts +21 -0
- package/src/server/__test-helpers/mock-graphql-request.ts +60 -0
- package/src/server/__test-helpers/request-mock.ts +169 -0
- package/src/server/__test-helpers/setup-embedded-flow.ts +30 -0
- package/src/server/__test-helpers/setup-fetch-flow.ts +29 -0
- package/src/server/__test-helpers/setup-non-embedded-flow.ts +31 -0
- package/src/server/__test-helpers/setup-valid-request.ts +78 -0
- package/src/server/__test-helpers/setup-valid-session.ts +28 -0
- package/src/server/__test-helpers/sign-request-cookie.ts +32 -0
- package/src/server/__test-helpers/test-config.ts +56 -0
- package/src/server/adapters/node/index.ts +13 -0
- package/src/server/adapters/vercel/index.ts +13 -0
- package/src/server/authenticate/admin/authenticate.admin.doc.example.ts +34 -0
- package/src/server/authenticate/admin/authenticate.ts +232 -0
- package/src/server/authenticate/admin/billing/cancel.ts +38 -0
- package/src/server/authenticate/admin/billing/check.ts +41 -0
- package/src/server/authenticate/admin/billing/create-usage-record.ts +36 -0
- package/src/server/authenticate/admin/billing/helpers.ts +42 -0
- package/src/server/authenticate/admin/billing/index.ts +6 -0
- package/src/server/authenticate/admin/billing/request.ts +64 -0
- package/src/server/authenticate/admin/billing/require.ts +58 -0
- package/src/server/authenticate/admin/billing/types.ts +617 -0
- package/src/server/authenticate/admin/billing/update-usage-subscription-capped-amount.ts +55 -0
- package/src/server/authenticate/admin/helpers/begin-auth.ts +17 -0
- package/src/server/authenticate/admin/helpers/create-admin-api-context.ts +20 -0
- package/src/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.ts +18 -0
- package/src/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.ts +25 -0
- package/src/server/authenticate/admin/helpers/handle-client-error.ts +45 -0
- package/src/server/authenticate/admin/helpers/index.ts +14 -0
- package/src/server/authenticate/admin/helpers/redirect-to-auth-page.ts +28 -0
- package/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts +23 -0
- package/src/server/authenticate/admin/helpers/redirect-to-install-page.ts +53 -0
- package/src/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.ts +21 -0
- package/src/server/authenticate/admin/helpers/redirect-with-app-bridge-headers.ts +13 -0
- package/src/server/authenticate/admin/helpers/redirect-with-exitiframe.ts +28 -0
- package/src/server/authenticate/admin/helpers/redirect.ts +164 -0
- package/src/server/authenticate/admin/helpers/render-app-bridge.ts +46 -0
- package/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +29 -0
- package/src/server/authenticate/admin/helpers/validate-redirect-url.ts +67 -0
- package/src/server/authenticate/admin/helpers/validate-shop-and-host-params.ts +46 -0
- package/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts +43 -0
- package/src/server/authenticate/admin/scope/client/revoke-scopes.ts +38 -0
- package/src/server/authenticate/admin/scope/factory.ts +21 -0
- package/src/server/authenticate/admin/scope/query.ts +51 -0
- package/src/server/authenticate/admin/scope/request.ts +32 -0
- package/src/server/authenticate/admin/scope/revoke.ts +48 -0
- package/src/server/authenticate/admin/scope/types.ts +127 -0
- package/src/server/authenticate/admin/strategies/auth-code-flow.ts +332 -0
- package/src/server/authenticate/admin/strategies/merchant-custom-app.ts +65 -0
- package/src/server/authenticate/admin/strategies/token-exchange.ts +188 -0
- package/src/server/authenticate/admin/strategies/types.ts +29 -0
- package/src/server/authenticate/admin/types.ts +226 -0
- package/src/server/authenticate/const.ts +15 -0
- package/src/server/authenticate/flow/authenticate.flow.doc.example.ts +39 -0
- package/src/server/authenticate/flow/authenticate.ts +68 -0
- package/src/server/authenticate/flow/types.ts +89 -0
- package/src/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.example.ts +52 -0
- package/src/server/authenticate/fulfillment-service/authenticate.ts +82 -0
- package/src/server/authenticate/fulfillment-service/types.ts +86 -0
- package/src/server/authenticate/helpers/add-response-headers.ts +43 -0
- package/src/server/authenticate/helpers/app-bridge-url.ts +10 -0
- package/src/server/authenticate/helpers/ensure-cors-headers.ts +38 -0
- package/src/server/authenticate/helpers/get-session-token-header.ts +11 -0
- package/src/server/authenticate/helpers/get-shop-from-request.ts +4 -0
- package/src/server/authenticate/helpers/idempotent-promise-handler.ts +45 -0
- package/src/server/authenticate/helpers/index.ts +10 -0
- package/src/server/authenticate/helpers/invalidate-access-token.ts +17 -0
- package/src/server/authenticate/helpers/reject-bot-request.ts +27 -0
- package/src/server/authenticate/helpers/respond-to-invalid-session-token.ts +28 -0
- package/src/server/authenticate/helpers/respond-to-options-request.ts +26 -0
- package/src/server/authenticate/helpers/validate-session-token.ts +40 -0
- package/src/server/authenticate/login/login.ts +53 -0
- package/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.example.ts +27 -0
- package/src/server/authenticate/public/appProxy/authenticate.ts +150 -0
- package/src/server/authenticate/public/appProxy/types.ts +219 -0
- package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.example.offers.ts +21 -0
- package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.example.ts +17 -0
- package/src/server/authenticate/public/checkout/authenticate.ts +13 -0
- package/src/server/authenticate/public/checkout/types.ts +69 -0
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.offers.ts +36 -0
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.ts +18 -0
- package/src/server/authenticate/public/customer-account/authenticate.ts +13 -0
- package/src/server/authenticate/public/customer-account/types.ts +69 -0
- package/src/server/authenticate/public/extension/authenticate.ts +54 -0
- package/src/server/authenticate/public/extension/types.ts +11 -0
- package/src/server/authenticate/public/factory.ts +24 -0
- package/src/server/authenticate/public/index.ts +1 -0
- package/src/server/authenticate/public/pos/authenticate.ts +8 -0
- package/src/server/authenticate/public/pos/types.ts +65 -0
- package/src/server/authenticate/public/types.ts +96 -0
- package/src/server/authenticate/webhooks/authenticate.ts +105 -0
- package/src/server/authenticate/webhooks/authenticate.webhooks.doc.example.ts +43 -0
- package/src/server/authenticate/webhooks/index.ts +1 -0
- package/src/server/authenticate/webhooks/register.ts +48 -0
- package/src/server/authenticate/webhooks/types.ts +267 -0
- package/src/server/boundary/error.tsx +14 -0
- package/src/server/boundary/headers.ts +15 -0
- package/src/server/boundary/index.ts +39 -0
- package/src/server/boundary/types.ts +4 -0
- package/src/server/clients/admin/factory.ts +22 -0
- package/src/server/clients/admin/graphql.ts +39 -0
- package/src/server/clients/admin/index.ts +2 -0
- package/src/server/clients/admin/types.ts +124 -0
- package/src/server/clients/index.ts +2 -0
- package/src/server/clients/storefront/factory.ts +32 -0
- package/src/server/clients/storefront/index.ts +2 -0
- package/src/server/clients/storefront/types.ts +82 -0
- package/src/server/clients/types.ts +47 -0
- package/src/server/config-types.ts +309 -0
- package/src/server/errors.ts +3 -0
- package/src/server/future/flags.ts +68 -0
- package/src/server/helpers/create-or-load-offline-session.ts +19 -0
- package/src/server/helpers/ensure-offline-token-is-not-expired.ts +32 -0
- package/src/server/helpers/ensure-valid-offline-session.ts +15 -0
- package/src/server/helpers/index.ts +4 -0
- package/src/server/helpers/refresh-token.ts +35 -0
- package/src/server/index.ts +24 -0
- package/src/server/override-logger.ts +42 -0
- package/src/server/shopify-app.ts +216 -0
- package/src/server/test-helpers/index.ts +1 -0
- package/src/server/test-helpers/test-config.ts +79 -0
- package/src/server/types-contexts.ts +44 -0
- package/src/server/types.ts +507 -0
- package/src/server/unauthenticated/admin/factory.ts +23 -0
- package/src/server/unauthenticated/admin/index.ts +1 -0
- package/src/server/unauthenticated/admin/types.ts +83 -0
- package/src/server/unauthenticated/storefront/factory.ts +28 -0
- package/src/server/unauthenticated/storefront/index.ts +1 -0
- package/src/server/unauthenticated/storefront/types.ts +109 -0
- package/src/server/unauthenticated/types.ts +65 -0
- package/src/server/version.ts +1 -0
- package/dist/ts/react/components/AppProvider/AppProvider.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProvider/AppProvider.doc.d.ts.map +0 -1
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts.map +0 -1
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts.map +0 -1
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/admin/authenticate.admin.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/admin/authenticate.admin.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/admin/billing/authenticate.admin.billing.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/admin/billing/authenticate.admin.billing.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/admin/scope/authenticate.admin.scopes.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/admin/scope/authenticate.admin.scopes.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/flow/authenticate.flow.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/flow/authenticate.flow.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/webhooks/authenticate.webhooks.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/webhooks/authenticate.webhooks.doc.d.ts.map +0 -1
- package/dist/ts/server/clients/admin/authenticate.admin.api.doc.d.ts +0 -4
- package/dist/ts/server/clients/admin/authenticate.admin.api.doc.d.ts.map +0 -1
- package/dist/ts/server/clients/storefront/authenticate.storefront.api.doc.d.ts +0 -4
- package/dist/ts/server/clients/storefront/authenticate.storefront.api.doc.d.ts.map +0 -1
- package/dist/ts/server/shopify-app.doc.d.ts +0 -4
- package/dist/ts/server/shopify-app.doc.d.ts.map +0 -1
- package/dist/ts/server/unauthenticated/admin/unauthenticated.admin.doc.d.ts +0 -4
- package/dist/ts/server/unauthenticated/admin/unauthenticated.admin.doc.d.ts.map +0 -1
- package/dist/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.d.ts +0 -4
- package/dist/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.d.ts.map +0 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {BasicParams} from '../../../types';
|
|
2
|
+
import {
|
|
3
|
+
respondToBotRequest,
|
|
4
|
+
respondToOptionsRequest,
|
|
5
|
+
getSessionTokenHeader,
|
|
6
|
+
validateSessionToken,
|
|
7
|
+
ensureCORSHeadersFactory,
|
|
8
|
+
getShopFromRequest,
|
|
9
|
+
} from '../../helpers';
|
|
10
|
+
|
|
11
|
+
import {AuthenticateExtension, ExtensionContext} from './types';
|
|
12
|
+
|
|
13
|
+
export function authenticateExtensionFactory(
|
|
14
|
+
params: BasicParams,
|
|
15
|
+
requestType: string,
|
|
16
|
+
): AuthenticateExtension {
|
|
17
|
+
return async function authenticateExtension(
|
|
18
|
+
request,
|
|
19
|
+
options = {},
|
|
20
|
+
): Promise<ExtensionContext> {
|
|
21
|
+
const {logger} = params;
|
|
22
|
+
|
|
23
|
+
const corsHeaders = options.corsHeaders ?? [];
|
|
24
|
+
|
|
25
|
+
respondToBotRequest(params, request);
|
|
26
|
+
respondToOptionsRequest(params, request, corsHeaders);
|
|
27
|
+
|
|
28
|
+
const sessionTokenHeader = getSessionTokenHeader(request);
|
|
29
|
+
|
|
30
|
+
logger.info(`Authenticating ${requestType} request`, {
|
|
31
|
+
shop: getShopFromRequest(request),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!sessionTokenHeader) {
|
|
35
|
+
logger.debug('Request did not contain a session token', {
|
|
36
|
+
shop: getShopFromRequest(request),
|
|
37
|
+
});
|
|
38
|
+
throw new Response(undefined, {
|
|
39
|
+
status: 401,
|
|
40
|
+
statusText: 'Unauthorized',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
sessionToken: await validateSessionToken(
|
|
46
|
+
params,
|
|
47
|
+
request,
|
|
48
|
+
sessionTokenHeader,
|
|
49
|
+
{checkAudience: false, retryRequest: false},
|
|
50
|
+
),
|
|
51
|
+
cors: ensureCORSHeadersFactory(params, request, corsHeaders),
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type AuthenticateExtension = (
|
|
2
|
+
request: Request,
|
|
3
|
+
options?: AuthenticateExtensionOptions,
|
|
4
|
+
) => Promise<ExtensionContext>;
|
|
5
|
+
|
|
6
|
+
export interface AuthenticateExtensionOptions {
|
|
7
|
+
corsHeaders?: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
11
|
+
export interface ExtensionContext {}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {BasicParams} from '../../types';
|
|
2
|
+
|
|
3
|
+
import {authenticateCheckoutFactory} from './checkout/authenticate';
|
|
4
|
+
import {authenticateAppProxyFactory} from './appProxy/authenticate';
|
|
5
|
+
import {authenticateCustomerAccountFactory} from './customer-account/authenticate';
|
|
6
|
+
import {AuthenticatePublic} from './types';
|
|
7
|
+
import {authenticatePOSFactory} from './pos/authenticate';
|
|
8
|
+
|
|
9
|
+
export function authenticatePublicFactory(params: BasicParams) {
|
|
10
|
+
const authenticateCheckout = authenticateCheckoutFactory(params);
|
|
11
|
+
const authenticateAppProxy = authenticateAppProxyFactory(params);
|
|
12
|
+
const authenticateCustomerAccount =
|
|
13
|
+
authenticateCustomerAccountFactory(params);
|
|
14
|
+
const authenticatePOS = authenticatePOSFactory(params);
|
|
15
|
+
|
|
16
|
+
const context: AuthenticatePublic = {
|
|
17
|
+
checkout: authenticateCheckout,
|
|
18
|
+
appProxy: authenticateAppProxy,
|
|
19
|
+
customerAccount: authenticateCustomerAccount,
|
|
20
|
+
pos: authenticatePOS,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return context;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './factory';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import {BasicParams} from '../../../types';
|
|
2
|
+
import {authenticateExtensionFactory} from '../extension/authenticate';
|
|
3
|
+
|
|
4
|
+
import type {AuthenticatePOS} from './types';
|
|
5
|
+
|
|
6
|
+
export function authenticatePOSFactory(params: BasicParams): AuthenticatePOS {
|
|
7
|
+
return authenticateExtensionFactory(params, 'pos') as AuthenticatePOS;
|
|
8
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {JwtPayload} from '@shopify/shopify-api';
|
|
2
|
+
|
|
3
|
+
import {EnsureCORSFunction} from '../../helpers/ensure-cors-headers';
|
|
4
|
+
|
|
5
|
+
export type AuthenticatePOS = (
|
|
6
|
+
request: Request,
|
|
7
|
+
options?: AuthenticatePOSOptions,
|
|
8
|
+
) => Promise<POSContext>;
|
|
9
|
+
|
|
10
|
+
export interface AuthenticatePOSOptions {
|
|
11
|
+
corsHeaders?: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Authenticated Context for a POS UI extension request
|
|
16
|
+
*/
|
|
17
|
+
export interface POSContext {
|
|
18
|
+
/**
|
|
19
|
+
* The decoded and validated session token for the request
|
|
20
|
+
*
|
|
21
|
+
* Refer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* <caption>Using the decoded session token.</caption>
|
|
25
|
+
* <description>Get store-specific data using the `sessionToken` object.</description>
|
|
26
|
+
* ```ts
|
|
27
|
+
* // app/routes/public/my-route.ts
|
|
28
|
+
* import { LoaderFunctionArgs, json } from "@remix-run/node";
|
|
29
|
+
* import { authenticate } from "../shopify.server";
|
|
30
|
+
* import { getMyAppData } from "~/db/model.server";
|
|
31
|
+
*
|
|
32
|
+
* export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
33
|
+
* const { sessionToken } = await authenticate.public.pos(
|
|
34
|
+
* request
|
|
35
|
+
* );
|
|
36
|
+
* return json(await getMyAppData({shop: sessionToken.dest}));
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
sessionToken: JwtPayload;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A function that ensures the CORS headers are set correctly for the response.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* <caption>Setting CORS headers for a public request.</caption>
|
|
47
|
+
* <description>Use the `cors` helper to ensure your app can respond to POS UI extension requests.</description>
|
|
48
|
+
* ```ts
|
|
49
|
+
* // app/routes/public/my-route.ts
|
|
50
|
+
* import { LoaderFunctionArgs, json } from "@remix-run/node";
|
|
51
|
+
* import { authenticate } from "../shopify.server";
|
|
52
|
+
* import { getMyAppData } from "~/db/model.server";
|
|
53
|
+
*
|
|
54
|
+
* export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
55
|
+
* const { sessionToken, cors } = await authenticate.public.pos(
|
|
56
|
+
* request,
|
|
57
|
+
* { corsHeaders: ["X-My-Custom-Header"] }
|
|
58
|
+
* );
|
|
59
|
+
* const data = await getMyAppData({shop: sessionToken.dest});
|
|
60
|
+
* return cors(json(data));
|
|
61
|
+
* };
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
cors: EnsureCORSFunction;
|
|
65
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type {AuthenticateCheckout} from './checkout/types';
|
|
2
|
+
import type {AuthenticateAppProxy} from './appProxy/types';
|
|
3
|
+
import type {AuthenticateCustomerAccount} from './customer-account/types';
|
|
4
|
+
import type {AuthenticatePOS} from './pos/types';
|
|
5
|
+
|
|
6
|
+
// Eventually this will be just the `{}` bit without `AuthenticateCheckout &`
|
|
7
|
+
// We have this is because in v1 public WAS the only public authenticate method
|
|
8
|
+
// But it became tightly coupled to authenticating Checkout requests.
|
|
9
|
+
// In V2 you will have only public.checkout() and public.appProxy(), no public()
|
|
10
|
+
|
|
11
|
+
export interface AuthenticatePublic {
|
|
12
|
+
/**
|
|
13
|
+
* Authenticate a request from a checkout extension
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <caption>Authenticating a checkout extension request</caption>
|
|
17
|
+
* ```ts
|
|
18
|
+
* // /app/routes/public/widgets.ts
|
|
19
|
+
* import { LoaderFunctionArgs, json } from "@remix-run/node";
|
|
20
|
+
* import { authenticate } from "../shopify.server";
|
|
21
|
+
*
|
|
22
|
+
* export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
23
|
+
* const { sessionToken, cors } = await authenticate.public.checkout(
|
|
24
|
+
* request,
|
|
25
|
+
* );
|
|
26
|
+
* return cors(json({my: "data", shop: sessionToken.dest}));
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
checkout: AuthenticateCheckout;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Authenticate a request from an app proxy
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* <caption>Authenticating an app proxy request</caption>
|
|
37
|
+
* ```ts
|
|
38
|
+
* // /app/routes/public/widgets.ts
|
|
39
|
+
* import { LoaderFunctionArgs, json } from "@remix-run/node";
|
|
40
|
+
* import { authenticate } from "../shopify.server";
|
|
41
|
+
*
|
|
42
|
+
* export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
43
|
+
* await authenticate.public.appProxy(
|
|
44
|
+
* request,
|
|
45
|
+
* );
|
|
46
|
+
*
|
|
47
|
+
* const {searchParams} = new URL(request.url);
|
|
48
|
+
* const shop = searchParams.get("shop");
|
|
49
|
+
* const customerId = searchParams.get("logged_in_customer_id")
|
|
50
|
+
*
|
|
51
|
+
* return json({my: "data", shop, customerId});
|
|
52
|
+
* };
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
appProxy: AuthenticateAppProxy;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Authenticate a request from a customer account extension
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* <caption>Authenticating a customer account extension request</caption>
|
|
62
|
+
* ```ts
|
|
63
|
+
* // /app/routes/public/widgets.ts
|
|
64
|
+
* import { LoaderFunctionArgs, json } from "@remix-run/node";
|
|
65
|
+
* import { authenticate } from "../shopify.server";
|
|
66
|
+
*
|
|
67
|
+
* export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
68
|
+
* const { sessionToken, cors } = await authenticate.public.customerAccount(
|
|
69
|
+
* request,
|
|
70
|
+
* );
|
|
71
|
+
* return cors(json({my: "data", shop: sessionToken.dest}));
|
|
72
|
+
* };
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
customerAccount: AuthenticateCustomerAccount;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Authenticate a request from a POS UI extension
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* <caption>Authenticating a POS UI extension request</caption>
|
|
82
|
+
* ```ts
|
|
83
|
+
* // /app/routes/public/widgets.ts
|
|
84
|
+
* import { LoaderFunctionArgs, json } from "@remix-run/node";
|
|
85
|
+
* import { authenticate } from "../shopify.server";
|
|
86
|
+
*
|
|
87
|
+
* export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
88
|
+
* const { sessionToken, cors } = await authenticate.public.pos(
|
|
89
|
+
* request,
|
|
90
|
+
* );
|
|
91
|
+
* return cors(json({my: "data", shop: sessionToken.dest}));
|
|
92
|
+
* };
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
pos: AuthenticatePOS;
|
|
96
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {WebhookValidationErrorReason, WebhookType} from '@shopify/shopify-api';
|
|
2
|
+
|
|
3
|
+
import type {BasicParams} from '../../types';
|
|
4
|
+
import {adminClientFactory} from '../../clients';
|
|
5
|
+
import {handleClientErrorFactory} from '../admin/helpers';
|
|
6
|
+
import {ensureValidOfflineSession} from '../../helpers';
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
AuthenticateWebhook,
|
|
10
|
+
WebhookContext,
|
|
11
|
+
WebhookContextWithoutSession,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
export function authenticateWebhookFactory<Topics extends string>(
|
|
15
|
+
params: BasicParams,
|
|
16
|
+
): AuthenticateWebhook<Topics> {
|
|
17
|
+
const {api, logger} = params;
|
|
18
|
+
|
|
19
|
+
return async function authenticate(
|
|
20
|
+
request: Request,
|
|
21
|
+
): Promise<WebhookContext<Topics>> {
|
|
22
|
+
if (request.method !== 'POST') {
|
|
23
|
+
logger.debug(
|
|
24
|
+
'Received a non-POST request for a webhook. Only POST requests are allowed.',
|
|
25
|
+
{url: request.url, method: request.method},
|
|
26
|
+
);
|
|
27
|
+
throw new Response(undefined, {
|
|
28
|
+
status: 405,
|
|
29
|
+
statusText: 'Method not allowed',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rawBody = await request.text();
|
|
34
|
+
|
|
35
|
+
const check = await api.webhooks.validate({
|
|
36
|
+
rawBody,
|
|
37
|
+
rawRequest: request,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!check.valid) {
|
|
41
|
+
if (check.reason === WebhookValidationErrorReason.InvalidHmac) {
|
|
42
|
+
logger.debug('Webhook HMAC validation failed', check);
|
|
43
|
+
throw new Response(undefined, {
|
|
44
|
+
status: 401,
|
|
45
|
+
statusText: 'Unauthorized',
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
logger.debug('Webhook validation failed', check);
|
|
49
|
+
throw new Response(undefined, {status: 400, statusText: 'Bad Request'});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const session = await ensureValidOfflineSession(params, check.domain);
|
|
53
|
+
|
|
54
|
+
let webhookContext: WebhookContextWithoutSession<Topics>;
|
|
55
|
+
|
|
56
|
+
if (check.webhookType === WebhookType.Webhooks) {
|
|
57
|
+
webhookContext = {
|
|
58
|
+
apiVersion: check.apiVersion,
|
|
59
|
+
shop: check.domain,
|
|
60
|
+
topic: check.topic as Topics,
|
|
61
|
+
webhookId: check.webhookId,
|
|
62
|
+
payload: JSON.parse(rawBody),
|
|
63
|
+
subTopic: check.subTopic || undefined,
|
|
64
|
+
session: undefined,
|
|
65
|
+
admin: undefined,
|
|
66
|
+
webhookType: check.webhookType,
|
|
67
|
+
name: check.name,
|
|
68
|
+
triggeredAt: check.triggeredAt,
|
|
69
|
+
eventId: check.eventId,
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
webhookContext = {
|
|
73
|
+
apiVersion: check.apiVersion,
|
|
74
|
+
shop: check.domain,
|
|
75
|
+
topic: check.topic as Topics,
|
|
76
|
+
webhookId: check.webhookId,
|
|
77
|
+
payload: JSON.parse(rawBody),
|
|
78
|
+
session: undefined,
|
|
79
|
+
admin: undefined,
|
|
80
|
+
webhookType: check.webhookType,
|
|
81
|
+
handle: check.handle,
|
|
82
|
+
action: check.action,
|
|
83
|
+
resourceId: check.resourceId,
|
|
84
|
+
triggeredAt: check.triggeredAt,
|
|
85
|
+
eventId: check.eventId,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!session) {
|
|
90
|
+
return webhookContext;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const admin = adminClientFactory({
|
|
94
|
+
params,
|
|
95
|
+
session,
|
|
96
|
+
handleClientError: handleClientErrorFactory({request}),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
...webhookContext,
|
|
101
|
+
session,
|
|
102
|
+
admin,
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {type ActionFunctionArgs} from '@remix-run/node';
|
|
2
|
+
|
|
3
|
+
import {authenticate} from '../shopify.server';
|
|
4
|
+
|
|
5
|
+
export const action = async ({request}: ActionFunctionArgs) => {
|
|
6
|
+
const {topic, admin, payload, session} = await authenticate.webhook(request);
|
|
7
|
+
|
|
8
|
+
// Webhook requests can trigger after an app is uninstalled
|
|
9
|
+
// If the app is already uninstalled, the session may be undefined.
|
|
10
|
+
if (!session) {
|
|
11
|
+
throw new Response();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
switch (topic) {
|
|
15
|
+
case 'PRODUCTS_UPDATE':
|
|
16
|
+
await admin.graphql(
|
|
17
|
+
`#graphql
|
|
18
|
+
mutation setMetafield($productId: ID!, $time: String!) {
|
|
19
|
+
metafieldsSet(metafields: {
|
|
20
|
+
ownerId: $productId
|
|
21
|
+
namespace: "my-app",
|
|
22
|
+
key: "webhook_received_at",
|
|
23
|
+
value: $time,
|
|
24
|
+
type: "string",
|
|
25
|
+
}) {
|
|
26
|
+
metafields {
|
|
27
|
+
key
|
|
28
|
+
value
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`,
|
|
33
|
+
{
|
|
34
|
+
variables: {
|
|
35
|
+
productId: payload.admin_graphql_api_id,
|
|
36
|
+
time: new Date().toISOString(),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return new Response();
|
|
43
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {registerWebhooksFactory} from './register';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type {BasicParams} from '../../types';
|
|
2
|
+
|
|
3
|
+
import type {RegisterWebhooksOptions} from './types';
|
|
4
|
+
|
|
5
|
+
export function registerWebhooksFactory({api, logger}: BasicParams) {
|
|
6
|
+
return async function registerWebhooks({session}: RegisterWebhooksOptions) {
|
|
7
|
+
return api.webhooks
|
|
8
|
+
.register({session})
|
|
9
|
+
.then((response) => {
|
|
10
|
+
Object.entries(response).forEach(([topic, topicResults]) => {
|
|
11
|
+
topicResults.forEach(({success, ...rest}) => {
|
|
12
|
+
if (success) {
|
|
13
|
+
logger.debug('Registered webhook', {
|
|
14
|
+
topic,
|
|
15
|
+
shop: session.shop,
|
|
16
|
+
operation: rest.operation,
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
logger.error('Failed to register webhook', {
|
|
20
|
+
topic,
|
|
21
|
+
shop: session.shop,
|
|
22
|
+
result: JSON.stringify(rest.result),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return response;
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
const graphQLErrors: {extensions: {code?: string}}[] =
|
|
32
|
+
error.body?.errors?.graphQLErrors || [];
|
|
33
|
+
|
|
34
|
+
const throttled = graphQLErrors.find(
|
|
35
|
+
({extensions: {code}}) => code === 'THROTTLED',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (throttled) {
|
|
39
|
+
logger.error('Failed to register webhooks', {
|
|
40
|
+
shop: session.shop,
|
|
41
|
+
error: JSON.stringify(error),
|
|
42
|
+
});
|
|
43
|
+
} else {
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
}
|