@shopify/shopify-app-remix 2.3.0 → 2.4.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/server/adapters/node/index.js +1 -0
- package/build/cjs/server/authenticate/admin/authenticate.js +17 -14
- package/build/cjs/server/authenticate/admin/billing/cancel.js +1 -1
- package/build/cjs/server/authenticate/admin/billing/require.js +1 -1
- package/build/cjs/server/authenticate/admin/helpers/create-admin-api-context.js +2 -5
- package/build/cjs/server/authenticate/admin/helpers/handle-client-error.js +8 -4
- package/build/cjs/server/authenticate/admin/helpers/redirect-to-bounce-page.js +4 -2
- package/build/cjs/server/authenticate/admin/helpers/redirect.js +1 -5
- package/build/cjs/server/authenticate/admin/helpers/trigger-after-auth-hook.js +2 -2
- package/build/cjs/server/authenticate/admin/strategies/auth-code-flow.js +31 -4
- package/build/cjs/server/authenticate/admin/strategies/token-exchange.js +155 -0
- package/build/cjs/server/authenticate/const.js +4 -0
- package/build/cjs/server/authenticate/helpers/idempotent-promise-handler.js +40 -0
- package/build/cjs/server/authenticate/helpers/respond-to-invalid-session-token.js +33 -0
- package/build/cjs/server/authenticate/login/login.js +5 -1
- package/build/cjs/server/authenticate/public/checkout/authenticate.js +1 -0
- package/build/cjs/server/authenticate/webhooks/authenticate.js +1 -1
- package/build/cjs/server/authenticate/webhooks/register.js +16 -0
- package/build/cjs/server/shopify-app.js +11 -6
- package/build/cjs/server/version.js +1 -1
- package/build/ts/react/components/AppProvider/AppProvider.js +1 -1
- package/build/ts/react/components/AppProvider/AppProvider.js.map +1 -1
- package/build/ts/server/__test-helpers/const.d.ts +2 -1
- package/build/ts/server/__test-helpers/const.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/const.js +3 -2
- package/build/ts/server/__test-helpers/const.js.map +1 -1
- package/build/ts/server/__test-helpers/expect-document-request-headers.js.map +1 -1
- package/build/ts/server/__test-helpers/expect-exit-iframe.js.map +1 -1
- package/build/ts/server/__test-helpers/get-thrown-response.js.map +1 -1
- package/build/ts/server/__test-helpers/request-mock.js.map +1 -1
- package/build/ts/server/__test-helpers/setup-valid-session.d.ts +1 -1
- package/build/ts/server/__test-helpers/setup-valid-session.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/setup-valid-session.js +5 -5
- package/build/ts/server/__test-helpers/setup-valid-session.js.map +1 -1
- package/build/ts/server/__test-helpers/test-config.d.ts +1 -0
- package/build/ts/server/__test-helpers/test-config.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/test-config.js +1 -0
- package/build/ts/server/__test-helpers/test-config.js.map +1 -1
- package/build/ts/server/adapters/node/index.js.map +1 -1
- package/build/ts/server/authenticate/admin/authenticate.d.ts +7 -1
- package/build/ts/server/authenticate/admin/authenticate.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/authenticate.js +14 -14
- package/build/ts/server/authenticate/admin/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/admin/billing/request.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts +2 -2
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js +2 -5
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.d.ts +2 -5
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.js +6 -5
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js +4 -2
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect-with-exitiframe.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect.js +0 -3
- package/build/ts/server/authenticate/admin/helpers/redirect.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/render-app-bridge.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts +2 -1
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js +2 -2
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.js.map +1 -1
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts +4 -2
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js +26 -4
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -1
- package/build/ts/server/authenticate/admin/strategies/token-exchange.d.ts +17 -0
- package/build/ts/server/authenticate/admin/strategies/token-exchange.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/token-exchange.js +163 -0
- package/build/ts/server/authenticate/admin/strategies/token-exchange.js.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/types.d.ts +16 -5
- package/build/ts/server/authenticate/admin/strategies/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/types.d.ts +0 -4
- package/build/ts/server/authenticate/admin/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/const.d.ts +3 -0
- package/build/ts/server/authenticate/const.d.ts.map +1 -1
- package/build/ts/server/authenticate/const.js +4 -1
- package/build/ts/server/authenticate/const.js.map +1 -1
- package/build/ts/server/authenticate/helpers/add-response-headers.js.map +1 -1
- package/build/ts/server/authenticate/helpers/ensure-cors-headers.js.map +1 -1
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.d.ts +12 -0
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.d.ts.map +1 -0
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.js +53 -0
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.js.map +1 -0
- package/build/ts/server/authenticate/helpers/index.d.ts +1 -0
- package/build/ts/server/authenticate/helpers/index.d.ts.map +1 -1
- package/build/ts/server/authenticate/helpers/index.js +1 -0
- package/build/ts/server/authenticate/helpers/index.js.map +1 -1
- package/build/ts/server/authenticate/helpers/reject-bot-request.js.map +1 -1
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.d.ts +9 -0
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.d.ts.map +1 -0
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.js +20 -0
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.js.map +1 -0
- package/build/ts/server/authenticate/helpers/respond-to-options-request.js.map +1 -1
- package/build/ts/server/authenticate/login/login.d.ts.map +1 -1
- package/build/ts/server/authenticate/login/login.js +6 -2
- package/build/ts/server/authenticate/login/login.js.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/public/checkout/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/public/factory.js.map +1 -1
- package/build/ts/server/authenticate/webhooks/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/webhooks/register.d.ts +1 -1
- package/build/ts/server/authenticate/webhooks/register.d.ts.map +1 -1
- package/build/ts/server/authenticate/webhooks/register.js +20 -1
- package/build/ts/server/authenticate/webhooks/register.js.map +1 -1
- package/build/ts/server/boundary/error.js.map +1 -1
- package/build/ts/server/boundary/headers.js.map +1 -1
- package/build/ts/server/clients/admin/rest.js.map +1 -1
- package/build/ts/server/config-types.d.ts +2 -0
- package/build/ts/server/config-types.d.ts.map +1 -1
- package/build/ts/server/future/flags.d.ts +6 -0
- package/build/ts/server/future/flags.d.ts.map +1 -1
- package/build/ts/server/override-logger.js.map +1 -1
- package/build/ts/server/shopify-app.d.ts.map +1 -1
- package/build/ts/server/shopify-app.js +10 -5
- package/build/ts/server/shopify-app.js.map +1 -1
- package/build/ts/server/types.d.ts +9 -4
- package/build/ts/server/types.d.ts.map +1 -1
- package/build/ts/server/types.js +2 -2
- package/build/ts/server/types.js.map +1 -1
- package/build/ts/server/unauthenticated/admin/factory.js.map +1 -1
- package/build/ts/server/unauthenticated/storefront/factory.js.map +1 -1
- package/build/ts/server/version.d.ts +1 -1
- package/build/ts/server/version.js +1 -1
- package/package.json +8 -12
|
@@ -4,6 +4,7 @@ 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
6
|
require('isbot');
|
|
7
|
+
require('@remix-run/server-runtime');
|
|
7
8
|
|
|
8
9
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
10
|
|
|
@@ -7,6 +7,7 @@ var validateSessionToken = require('../helpers/validate-session-token.js');
|
|
|
7
7
|
var getSessionTokenHeader = require('../helpers/get-session-token-header.js');
|
|
8
8
|
var rejectBotRequest = require('../helpers/reject-bot-request.js');
|
|
9
9
|
var respondToOptionsRequest = require('../helpers/respond-to-options-request.js');
|
|
10
|
+
require('@remix-run/server-runtime');
|
|
10
11
|
var cancel = require('./billing/cancel.js');
|
|
11
12
|
var require$1 = require('./billing/require.js');
|
|
12
13
|
var request = require('./billing/request.js');
|
|
@@ -14,7 +15,6 @@ var createAdminApiContext = require('./helpers/create-admin-api-context.js');
|
|
|
14
15
|
var ensureAppIsEmbeddedIfRequired = require('./helpers/ensure-app-is-embedded-if-required.js');
|
|
15
16
|
var ensureSessionTokenSearchParamIfRequired = require('./helpers/ensure-session-token-search-param-if-required.js');
|
|
16
17
|
require('@shopify/shopify-api');
|
|
17
|
-
require('@remix-run/server-runtime');
|
|
18
18
|
var redirect = require('./helpers/redirect.js');
|
|
19
19
|
var renderAppBridge = require('./helpers/render-app-bridge.js');
|
|
20
20
|
var validateShopAndHostParams = require('./helpers/validate-shop-and-host-params.js');
|
|
@@ -55,13 +55,9 @@ function authStrategyFactory({
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
function createContext(request$1, session, sessionToken) {
|
|
58
|
+
function createContext(request$1, session, authStrategy, sessionToken) {
|
|
59
59
|
const context = {
|
|
60
|
-
admin: createAdminApiContext.createAdminApiContext(request$1,
|
|
61
|
-
api,
|
|
62
|
-
logger,
|
|
63
|
-
config
|
|
64
|
-
}),
|
|
60
|
+
admin: createAdminApiContext.createAdminApiContext(session, params, authStrategy.handleClientError(request$1)),
|
|
65
61
|
billing: {
|
|
66
62
|
require: require$1.requireBillingFactory(params, request$1, session),
|
|
67
63
|
request: request.requestBillingFactory(params, request$1, session),
|
|
@@ -99,18 +95,23 @@ function authStrategyFactory({
|
|
|
99
95
|
const {
|
|
100
96
|
payload,
|
|
101
97
|
shop,
|
|
102
|
-
sessionId
|
|
98
|
+
sessionId,
|
|
99
|
+
sessionToken
|
|
103
100
|
} = await getSessionTokenContext(params, request);
|
|
104
101
|
logger.debug('Loading session from storage', {
|
|
105
102
|
sessionId
|
|
106
103
|
});
|
|
107
104
|
const existingSession = sessionId ? await config.sessionStorage.loadSession(sessionId) : undefined;
|
|
108
|
-
const session = await strategy.authenticate(request,
|
|
105
|
+
const session = await strategy.authenticate(request, {
|
|
106
|
+
session: existingSession,
|
|
107
|
+
sessionToken,
|
|
108
|
+
shop
|
|
109
|
+
});
|
|
109
110
|
logger.debug('Request is valid, loaded session from session token', {
|
|
110
111
|
shop: session.shop,
|
|
111
112
|
isOnline: session.isOnline
|
|
112
113
|
});
|
|
113
|
-
return createContext(request, session, payload);
|
|
114
|
+
return createContext(request, session, strategy, payload);
|
|
114
115
|
} catch (errorOrResponse) {
|
|
115
116
|
if (errorOrResponse instanceof Response) {
|
|
116
117
|
ensureCorsHeaders.ensureCORSHeadersFactory(params, request)(errorOrResponse);
|
|
@@ -129,10 +130,10 @@ async function getSessionTokenContext(params, request) {
|
|
|
129
130
|
const searchParamSessionToken = getSessionTokenHeader.getSessionTokenFromUrlParam(request);
|
|
130
131
|
const sessionToken = headerSessionToken || searchParamSessionToken;
|
|
131
132
|
logger.debug('Attempting to authenticate session token', {
|
|
132
|
-
sessionToken: {
|
|
133
|
+
sessionToken: JSON.stringify({
|
|
133
134
|
header: headerSessionToken,
|
|
134
135
|
search: searchParamSessionToken
|
|
135
|
-
}
|
|
136
|
+
})
|
|
136
137
|
});
|
|
137
138
|
if (config.isEmbeddedApp) {
|
|
138
139
|
const payload = await validateSessionToken.validateSessionToken({
|
|
@@ -150,7 +151,8 @@ async function getSessionTokenContext(params, request) {
|
|
|
150
151
|
return {
|
|
151
152
|
shop,
|
|
152
153
|
payload,
|
|
153
|
-
sessionId
|
|
154
|
+
sessionId,
|
|
155
|
+
sessionToken
|
|
154
156
|
};
|
|
155
157
|
}
|
|
156
158
|
const url = new URL(request.url);
|
|
@@ -162,7 +164,8 @@ async function getSessionTokenContext(params, request) {
|
|
|
162
164
|
return {
|
|
163
165
|
shop,
|
|
164
166
|
sessionId,
|
|
165
|
-
payload: undefined
|
|
167
|
+
payload: undefined,
|
|
168
|
+
sessionToken
|
|
166
169
|
};
|
|
167
170
|
}
|
|
168
171
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
-
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
7
6
|
require('@remix-run/server-runtime');
|
|
7
|
+
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
8
8
|
|
|
9
9
|
function cancelBillingFactory(params, request, session) {
|
|
10
10
|
return async function cancelBilling(options) {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
-
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
7
6
|
require('@remix-run/server-runtime');
|
|
7
|
+
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
8
8
|
|
|
9
9
|
function requireBillingFactory(params, request, session) {
|
|
10
10
|
const {
|
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var factory = require('../../../clients/admin/factory.js');
|
|
6
|
-
var handleClientError = require('./handle-client-error.js');
|
|
7
6
|
|
|
8
|
-
function createAdminApiContext(
|
|
7
|
+
function createAdminApiContext(session, params, handleClientError) {
|
|
9
8
|
return factory.adminClientFactory({
|
|
10
9
|
session,
|
|
11
10
|
params,
|
|
12
|
-
handleClientError
|
|
13
|
-
request
|
|
14
|
-
})
|
|
11
|
+
handleClientError
|
|
15
12
|
});
|
|
16
13
|
}
|
|
17
14
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
-
var redirectToAuthPage = require('./redirect-to-auth-page.js');
|
|
7
6
|
|
|
8
7
|
function handleClientErrorFactory({
|
|
9
|
-
request
|
|
8
|
+
request,
|
|
9
|
+
onError
|
|
10
10
|
}) {
|
|
11
11
|
return async function handleClientError({
|
|
12
12
|
error,
|
|
@@ -22,8 +22,12 @@ function handleClientErrorFactory({
|
|
|
22
22
|
statusText: error.response.statusText,
|
|
23
23
|
body: JSON.stringify(error.response.body)
|
|
24
24
|
});
|
|
25
|
-
if (
|
|
26
|
-
|
|
25
|
+
if (onError) {
|
|
26
|
+
await onError({
|
|
27
|
+
request,
|
|
28
|
+
session,
|
|
29
|
+
error
|
|
30
|
+
});
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
// forward a minimal copy of the upstream HTTP response instead of an Error:
|
|
@@ -11,12 +11,14 @@ const redirectToBouncePage = (params, url) => {
|
|
|
11
11
|
|
|
12
12
|
// Make sure we always point to the configured app URL so it also works behind reverse proxies (that alter the Host
|
|
13
13
|
// header).
|
|
14
|
-
|
|
14
|
+
const searchParams = url.searchParams;
|
|
15
|
+
searchParams.delete('id_token');
|
|
16
|
+
searchParams.set('shopify-reload', `${config.appUrl}${url.pathname}?${searchParams.toString()}`);
|
|
15
17
|
|
|
16
18
|
// eslint-disable-next-line no-warning-comments
|
|
17
19
|
// TODO Make sure this works on chrome without a tunnel (weird HTTPS redirect issue)
|
|
18
20
|
// https://github.com/orgs/Shopify/projects/6899/views/1?pane=issue&itemId=28376650
|
|
19
|
-
throw serverRuntime.redirect(`${config.auth.patchSessionTokenPath}
|
|
21
|
+
throw serverRuntime.redirect(`${config.auth.patchSessionTokenPath}?${searchParams.toString()}`);
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
exports.redirectToBouncePage = redirectToBouncePage;
|
|
@@ -49,13 +49,9 @@ function isBounceRequest(request) {
|
|
|
49
49
|
return Boolean(getSessionTokenHeader.getSessionTokenHeader(request)) && request.headers.has('X-Shopify-Bounce');
|
|
50
50
|
}
|
|
51
51
|
function isDataRequest(request) {
|
|
52
|
-
const {
|
|
53
|
-
searchParams
|
|
54
|
-
} = new URL(request.url);
|
|
55
52
|
const isGet = request.method === 'GET';
|
|
56
53
|
const sessionTokenHeader = Boolean(getSessionTokenHeader.getSessionTokenHeader(request));
|
|
57
|
-
|
|
58
|
-
return sessionTokenHeader && !sessionTokenSearchParam && !isBounceRequest(request) && (!isEmbeddedRequest(request) || !isGet);
|
|
54
|
+
return sessionTokenHeader && !isBounceRequest(request) && (!isEmbeddedRequest(request) || !isGet);
|
|
59
55
|
}
|
|
60
56
|
function isEmbeddedRequest(request) {
|
|
61
57
|
const {
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var createAdminApiContext = require('./create-admin-api-context.js');
|
|
6
6
|
|
|
7
|
-
async function triggerAfterAuthHook(params, session, request) {
|
|
7
|
+
async function triggerAfterAuthHook(params, session, request, authStrategy) {
|
|
8
8
|
const {
|
|
9
9
|
config,
|
|
10
10
|
logger
|
|
@@ -13,7 +13,7 @@ async function triggerAfterAuthHook(params, session, request) {
|
|
|
13
13
|
logger.info('Running afterAuth hook');
|
|
14
14
|
await config.hooks.afterAuth({
|
|
15
15
|
session,
|
|
16
|
-
admin: createAdminApiContext.createAdminApiContext(
|
|
16
|
+
admin: createAdminApiContext.createAdminApiContext(session, params, authStrategy.handleClientError(request))
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -4,9 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
6
|
var beginAuth = require('../helpers/begin-auth.js');
|
|
7
|
-
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
8
7
|
var redirectToShopifyOrAppRoot = require('../helpers/redirect-to-shopify-or-app-root.js');
|
|
9
8
|
require('@remix-run/server-runtime');
|
|
9
|
+
var handleClientError = require('../helpers/handle-client-error.js');
|
|
10
|
+
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
10
11
|
var redirectWithExitiframe = require('../helpers/redirect-with-exitiframe.js');
|
|
11
12
|
var getSessionTokenHeader = require('../../helpers/get-session-token-header.js');
|
|
12
13
|
var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
|
|
@@ -52,12 +53,16 @@ class AuthCodeFlowStrategy {
|
|
|
52
53
|
await this.ensureInstalledOnShop(request);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
async authenticate(request,
|
|
56
|
+
async authenticate(request, sessionContext) {
|
|
56
57
|
const {
|
|
57
58
|
api,
|
|
58
59
|
config,
|
|
59
60
|
logger
|
|
60
61
|
} = this;
|
|
62
|
+
const {
|
|
63
|
+
shop,
|
|
64
|
+
session
|
|
65
|
+
} = sessionContext;
|
|
61
66
|
if (!session) {
|
|
62
67
|
logger.debug('No session found, redirecting to OAuth', {
|
|
63
68
|
shop
|
|
@@ -82,6 +87,28 @@ class AuthCodeFlowStrategy {
|
|
|
82
87
|
});
|
|
83
88
|
return session;
|
|
84
89
|
}
|
|
90
|
+
handleClientError(request) {
|
|
91
|
+
const {
|
|
92
|
+
api,
|
|
93
|
+
config,
|
|
94
|
+
logger
|
|
95
|
+
} = this;
|
|
96
|
+
return handleClientError.handleClientErrorFactory({
|
|
97
|
+
request,
|
|
98
|
+
onError: async ({
|
|
99
|
+
session,
|
|
100
|
+
error
|
|
101
|
+
}) => {
|
|
102
|
+
if (error.response.code === 401) {
|
|
103
|
+
throw await redirectToAuthPage.redirectToAuthPage({
|
|
104
|
+
api,
|
|
105
|
+
config,
|
|
106
|
+
logger
|
|
107
|
+
}, request, session.shop);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
85
112
|
async ensureInstalledOnShop(request) {
|
|
86
113
|
const {
|
|
87
114
|
api,
|
|
@@ -197,7 +224,7 @@ class AuthCodeFlowStrategy {
|
|
|
197
224
|
api,
|
|
198
225
|
config,
|
|
199
226
|
logger
|
|
200
|
-
}, session, request);
|
|
227
|
+
}, session, request, this);
|
|
201
228
|
throw await redirectToShopifyOrAppRoot.redirectToShopifyOrAppRoot(request, {
|
|
202
229
|
api,
|
|
203
230
|
config,
|
|
@@ -293,7 +320,7 @@ class AuthCodeFlowStrategy {
|
|
|
293
320
|
shop
|
|
294
321
|
};
|
|
295
322
|
if (error.response) {
|
|
296
|
-
context.response = JSON.stringify(error.
|
|
323
|
+
context.response = JSON.stringify(error.body);
|
|
297
324
|
}
|
|
298
325
|
logger.error(`Unexpected error during session validation: ${error.message}`, context);
|
|
299
326
|
throw new Response(undefined, {
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
+
require('isbot');
|
|
7
|
+
var respondToInvalidSessionToken = require('../../helpers/respond-to-invalid-session-token.js');
|
|
8
|
+
require('@remix-run/server-runtime');
|
|
9
|
+
var handleClientError = require('../helpers/handle-client-error.js');
|
|
10
|
+
var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
|
|
11
|
+
|
|
12
|
+
class TokenExchangeStrategy {
|
|
13
|
+
constructor({
|
|
14
|
+
api,
|
|
15
|
+
config,
|
|
16
|
+
logger
|
|
17
|
+
}) {
|
|
18
|
+
this.api = void 0;
|
|
19
|
+
this.config = void 0;
|
|
20
|
+
this.logger = void 0;
|
|
21
|
+
this.api = api;
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.logger = logger;
|
|
24
|
+
}
|
|
25
|
+
async respondToOAuthRequests(_request) {}
|
|
26
|
+
async authenticate(request, sessionContext) {
|
|
27
|
+
const {
|
|
28
|
+
api,
|
|
29
|
+
config,
|
|
30
|
+
logger
|
|
31
|
+
} = this;
|
|
32
|
+
const {
|
|
33
|
+
shop,
|
|
34
|
+
session,
|
|
35
|
+
sessionToken
|
|
36
|
+
} = sessionContext;
|
|
37
|
+
if (!sessionToken) throw new shopifyApi.InvalidJwtError();
|
|
38
|
+
if (!session || session.isExpired()) {
|
|
39
|
+
logger.info('No valid session found');
|
|
40
|
+
logger.info('Requesting offline access token');
|
|
41
|
+
const {
|
|
42
|
+
session: offlineSession
|
|
43
|
+
} = await this.exchangeToken({
|
|
44
|
+
request,
|
|
45
|
+
sessionToken,
|
|
46
|
+
shop,
|
|
47
|
+
requestedTokenType: shopifyApi.RequestedTokenType.OfflineAccessToken
|
|
48
|
+
});
|
|
49
|
+
await config.sessionStorage.storeSession(offlineSession);
|
|
50
|
+
let newSession = offlineSession;
|
|
51
|
+
if (config.useOnlineTokens) {
|
|
52
|
+
logger.info('Requesting online access token');
|
|
53
|
+
const {
|
|
54
|
+
session: onlineSession
|
|
55
|
+
} = await this.exchangeToken({
|
|
56
|
+
request,
|
|
57
|
+
sessionToken,
|
|
58
|
+
shop,
|
|
59
|
+
requestedTokenType: shopifyApi.RequestedTokenType.OnlineAccessToken
|
|
60
|
+
});
|
|
61
|
+
await config.sessionStorage.storeSession(onlineSession);
|
|
62
|
+
newSession = onlineSession;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await this.handleAfterAuthHook({
|
|
66
|
+
api,
|
|
67
|
+
config,
|
|
68
|
+
logger
|
|
69
|
+
}, newSession, request, sessionToken);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new Response(undefined, {
|
|
72
|
+
status: 500,
|
|
73
|
+
statusText: 'Internal Server Error'
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return newSession;
|
|
77
|
+
}
|
|
78
|
+
return session;
|
|
79
|
+
}
|
|
80
|
+
handleClientError(request) {
|
|
81
|
+
const {
|
|
82
|
+
api,
|
|
83
|
+
config,
|
|
84
|
+
logger
|
|
85
|
+
} = this;
|
|
86
|
+
return handleClientError.handleClientErrorFactory({
|
|
87
|
+
request,
|
|
88
|
+
onError: async ({
|
|
89
|
+
session,
|
|
90
|
+
error
|
|
91
|
+
}) => {
|
|
92
|
+
if (error.response.code === 401) {
|
|
93
|
+
config.sessionStorage.deleteSession(session.id);
|
|
94
|
+
respondToInvalidSessionToken.respondToInvalidSessionToken({
|
|
95
|
+
params: {
|
|
96
|
+
config,
|
|
97
|
+
api,
|
|
98
|
+
logger
|
|
99
|
+
},
|
|
100
|
+
request
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async exchangeToken({
|
|
107
|
+
request,
|
|
108
|
+
shop,
|
|
109
|
+
sessionToken,
|
|
110
|
+
requestedTokenType
|
|
111
|
+
}) {
|
|
112
|
+
const {
|
|
113
|
+
api,
|
|
114
|
+
config,
|
|
115
|
+
logger
|
|
116
|
+
} = this;
|
|
117
|
+
try {
|
|
118
|
+
return await api.auth.tokenExchange({
|
|
119
|
+
sessionToken,
|
|
120
|
+
shop,
|
|
121
|
+
requestedTokenType
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
var _error$response$body;
|
|
125
|
+
if (error instanceof shopifyApi.InvalidJwtError || error instanceof shopifyApi.HttpResponseError && error.response.code === 400 && ((_error$response$body = error.response.body) === null || _error$response$body === void 0 ? void 0 : _error$response$body.error) === 'invalid_subject_token') {
|
|
126
|
+
throw respondToInvalidSessionToken.respondToInvalidSessionToken({
|
|
127
|
+
params: {
|
|
128
|
+
api,
|
|
129
|
+
config,
|
|
130
|
+
logger
|
|
131
|
+
},
|
|
132
|
+
request,
|
|
133
|
+
retryRequest: true
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
throw new Response(undefined, {
|
|
137
|
+
status: 500,
|
|
138
|
+
statusText: 'Internal Server Error'
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async handleAfterAuthHook(params, session, request, sessionToken) {
|
|
143
|
+
const {
|
|
144
|
+
config
|
|
145
|
+
} = params;
|
|
146
|
+
await config.idempotentPromiseHandler.handlePromise({
|
|
147
|
+
promiseFunction: () => {
|
|
148
|
+
return triggerAfterAuthHook.triggerAfterAuthHook(params, session, request, this);
|
|
149
|
+
},
|
|
150
|
+
identifier: sessionToken
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
exports.TokenExchangeStrategy = TokenExchangeStrategy;
|
|
@@ -4,6 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
const APP_BRIDGE_URL = 'https://cdn.shopify.com/shopifycloud/app-bridge.js';
|
|
6
6
|
const REAUTH_URL_HEADER = 'X-Shopify-API-Request-Failure-Reauthorize-Url';
|
|
7
|
+
const RETRY_INVALID_SESSION_HEADER = {
|
|
8
|
+
'X-Shopify-Retry-Invalid-Session-Request': '1'
|
|
9
|
+
};
|
|
7
10
|
|
|
8
11
|
exports.APP_BRIDGE_URL = APP_BRIDGE_URL;
|
|
9
12
|
exports.REAUTH_URL_HEADER = REAUTH_URL_HEADER;
|
|
13
|
+
exports.RETRY_INVALID_SESSION_HEADER = RETRY_INVALID_SESSION_HEADER;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const IDENTIFIER_TTL_MS = 60000;
|
|
6
|
+
class IdempotentPromiseHandler {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.identifiers = void 0;
|
|
9
|
+
this.identifiers = new Map();
|
|
10
|
+
}
|
|
11
|
+
async handlePromise({
|
|
12
|
+
promiseFunction,
|
|
13
|
+
identifier
|
|
14
|
+
}) {
|
|
15
|
+
try {
|
|
16
|
+
if (this.isPromiseRunnable(identifier)) {
|
|
17
|
+
await promiseFunction();
|
|
18
|
+
}
|
|
19
|
+
} finally {
|
|
20
|
+
this.clearStaleIdentifiers();
|
|
21
|
+
}
|
|
22
|
+
return Promise.resolve();
|
|
23
|
+
}
|
|
24
|
+
isPromiseRunnable(identifier) {
|
|
25
|
+
if (!this.identifiers.has(identifier)) {
|
|
26
|
+
this.identifiers.set(identifier, Date.now());
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
async clearStaleIdentifiers() {
|
|
32
|
+
this.identifiers.forEach((date, identifier, map) => {
|
|
33
|
+
if (Date.now() - date > IDENTIFIER_TTL_MS) {
|
|
34
|
+
map.delete(identifier);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.IdempotentPromiseHandler = IdempotentPromiseHandler;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var redirectToBouncePage = require('../admin/helpers/redirect-to-bounce-page.js');
|
|
6
|
+
var _const = require('../const.js');
|
|
7
|
+
|
|
8
|
+
function respondToInvalidSessionToken({
|
|
9
|
+
params,
|
|
10
|
+
request,
|
|
11
|
+
retryRequest = false
|
|
12
|
+
}) {
|
|
13
|
+
const {
|
|
14
|
+
api,
|
|
15
|
+
logger,
|
|
16
|
+
config
|
|
17
|
+
} = params;
|
|
18
|
+
const isDocumentRequest = !request.headers.get('authorization');
|
|
19
|
+
if (isDocumentRequest) {
|
|
20
|
+
return redirectToBouncePage.redirectToBouncePage({
|
|
21
|
+
api,
|
|
22
|
+
logger,
|
|
23
|
+
config
|
|
24
|
+
}, new URL(request.url));
|
|
25
|
+
}
|
|
26
|
+
throw new Response(undefined, {
|
|
27
|
+
status: 401,
|
|
28
|
+
statusText: 'Unauthorized',
|
|
29
|
+
headers: retryRequest ? _const.RETRY_INVALID_SESSION_HEADER : {}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
exports.respondToInvalidSessionToken = respondToInvalidSessionToken;
|
|
@@ -37,7 +37,11 @@ function loginFactory(params) {
|
|
|
37
37
|
shop: types.LoginErrorType.InvalidShop
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
const
|
|
40
|
+
const authPath = `${config.appUrl}${config.auth.path}?shop=${sanitizedShop}`;
|
|
41
|
+
const adminPath = api.utils.legacyUrlToShopAdminUrl(sanitizedShop);
|
|
42
|
+
const installPath = `https://${adminPath}/oauth/install?client_id=${config.apiKey}`;
|
|
43
|
+
const shouldInstall = config.isEmbeddedApp && config.future.unstable_newEmbeddedAuthStrategy;
|
|
44
|
+
const redirectUrl = shouldInstall ? installPath : authPath;
|
|
41
45
|
logger.info(`Redirecting login request to ${redirectUrl}`, {
|
|
42
46
|
shop: sanitizedShop
|
|
43
47
|
});
|
|
@@ -7,6 +7,7 @@ var validateSessionToken = require('../../helpers/validate-session-token.js');
|
|
|
7
7
|
var getSessionTokenHeader = require('../../helpers/get-session-token-header.js');
|
|
8
8
|
var rejectBotRequest = require('../../helpers/reject-bot-request.js');
|
|
9
9
|
var respondToOptionsRequest = require('../../helpers/respond-to-options-request.js');
|
|
10
|
+
require('@remix-run/server-runtime');
|
|
10
11
|
|
|
11
12
|
function authenticateCheckoutFactory(params) {
|
|
12
13
|
return async function authenticateCheckout(request, options = {}) {
|
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
6
|
var factory = require('../../clients/admin/factory.js');
|
|
7
|
-
var handleClientError = require('../admin/helpers/handle-client-error.js');
|
|
8
7
|
require('@remix-run/server-runtime');
|
|
8
|
+
var handleClientError = require('../admin/helpers/handle-client-error.js');
|
|
9
9
|
|
|
10
10
|
function authenticateWebhookFactory(params) {
|
|
11
11
|
const {
|
|
@@ -33,6 +33,22 @@ function registerWebhooksFactory({
|
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
35
|
return response;
|
|
36
|
+
}).catch(error => {
|
|
37
|
+
var _error$body, _error$body$errors;
|
|
38
|
+
const graphQLErrors = ((_error$body = error.body) === null || _error$body === void 0 ? void 0 : (_error$body$errors = _error$body.errors) === null || _error$body$errors === void 0 ? void 0 : _error$body$errors.graphQLErrors) || [];
|
|
39
|
+
const throttled = graphQLErrors.find(({
|
|
40
|
+
extensions: {
|
|
41
|
+
code
|
|
42
|
+
}
|
|
43
|
+
}) => code === 'THROTTLED');
|
|
44
|
+
if (throttled) {
|
|
45
|
+
logger.error('Failed to register webhooks', {
|
|
46
|
+
shop: session.shop,
|
|
47
|
+
error: JSON.stringify(error)
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
36
52
|
});
|
|
37
53
|
};
|
|
38
54
|
}
|
|
@@ -12,11 +12,14 @@ var authenticate$1 = 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
14
|
require('isbot');
|
|
15
|
+
require('@remix-run/server-runtime');
|
|
15
16
|
var login = require('./authenticate/login/login.js');
|
|
16
17
|
var factory$1 = require('./unauthenticated/admin/factory.js');
|
|
17
18
|
var factory = require('./authenticate/public/factory.js');
|
|
18
19
|
var factory$2 = require('./unauthenticated/storefront/factory.js');
|
|
19
20
|
var authCodeFlow = require('./authenticate/admin/strategies/auth-code-flow.js');
|
|
21
|
+
var tokenExchange = require('./authenticate/admin/strategies/token-exchange.js');
|
|
22
|
+
var idempotentPromiseHandler = require('./authenticate/helpers/idempotent-promise-handler.js');
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* Creates an object your app will use to interact with Shopify.
|
|
@@ -51,10 +54,9 @@ function shopifyApp(appConfig) {
|
|
|
51
54
|
config,
|
|
52
55
|
logger
|
|
53
56
|
};
|
|
54
|
-
const oauth = new authCodeFlow.AuthCodeFlowStrategy(params);
|
|
55
57
|
const authStrategy = authenticate.authStrategyFactory({
|
|
56
58
|
...params,
|
|
57
|
-
strategy:
|
|
59
|
+
strategy: config.future.unstable_newEmbeddedAuthStrategy && config.isEmbeddedApp ? new tokenExchange.TokenExchangeStrategy(params) : new authCodeFlow.AuthCodeFlowStrategy(params)
|
|
58
60
|
});
|
|
59
61
|
const shopify = {
|
|
60
62
|
sessionStorage: config.sessionStorage,
|
|
@@ -82,7 +84,7 @@ function isSingleMerchantApp(_shopify, config) {
|
|
|
82
84
|
return config.distribution === types.AppDistribution.SingleMerchant;
|
|
83
85
|
}
|
|
84
86
|
function deriveApi(appConfig) {
|
|
85
|
-
var _appConfig$isEmbedded, _appConfig$apiVersion;
|
|
87
|
+
var _appConfig$isEmbedded, _appConfig$apiVersion, _appConfig$future;
|
|
86
88
|
let appUrl;
|
|
87
89
|
try {
|
|
88
90
|
appUrl = new URL(appConfig.appUrl);
|
|
@@ -108,11 +110,13 @@ function deriveApi(appConfig) {
|
|
|
108
110
|
isEmbeddedApp: (_appConfig$isEmbedded = appConfig.isEmbeddedApp) !== null && _appConfig$isEmbedded !== void 0 ? _appConfig$isEmbedded : true,
|
|
109
111
|
apiVersion: (_appConfig$apiVersion = appConfig.apiVersion) !== null && _appConfig$apiVersion !== void 0 ? _appConfig$apiVersion : shopifyApi.LATEST_API_VERSION,
|
|
110
112
|
isCustomStoreApp: appConfig.distribution === types.AppDistribution.ShopifyAdmin,
|
|
111
|
-
future: {
|
|
113
|
+
future: {
|
|
114
|
+
unstable_tokenExchange: (_appConfig$future = appConfig.future) === null || _appConfig$future === void 0 ? void 0 : _appConfig$future.unstable_newEmbeddedAuthStrategy
|
|
115
|
+
}
|
|
112
116
|
});
|
|
113
117
|
}
|
|
114
118
|
function deriveConfig(appConfig, apiConfig) {
|
|
115
|
-
var _appConfig$distributi, _appConfig$useOnlineT, _appConfig$hooks, _appConfig$
|
|
119
|
+
var _appConfig$distributi, _appConfig$useOnlineT, _appConfig$hooks, _appConfig$future2;
|
|
116
120
|
if (!appConfig.sessionStorage) {
|
|
117
121
|
throw new shopifyApi.ShopifyError('Please provide a valid session storage. Refer to https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options for options.');
|
|
118
122
|
}
|
|
@@ -121,11 +125,12 @@ function deriveConfig(appConfig, apiConfig) {
|
|
|
121
125
|
return {
|
|
122
126
|
...appConfig,
|
|
123
127
|
...apiConfig,
|
|
128
|
+
idempotentPromiseHandler: new idempotentPromiseHandler.IdempotentPromiseHandler(),
|
|
124
129
|
canUseLoginForm: appConfig.distribution !== types.AppDistribution.ShopifyAdmin,
|
|
125
130
|
useOnlineTokens: (_appConfig$useOnlineT = appConfig.useOnlineTokens) !== null && _appConfig$useOnlineT !== void 0 ? _appConfig$useOnlineT : false,
|
|
126
131
|
hooks: (_appConfig$hooks = appConfig.hooks) !== null && _appConfig$hooks !== void 0 ? _appConfig$hooks : {},
|
|
127
132
|
sessionStorage: appConfig.sessionStorage,
|
|
128
|
-
future: (_appConfig$
|
|
133
|
+
future: (_appConfig$future2 = appConfig.future) !== null && _appConfig$future2 !== void 0 ? _appConfig$future2 : {},
|
|
129
134
|
auth: {
|
|
130
135
|
path: authPathPrefix,
|
|
131
136
|
callbackPath: `${authPathPrefix}/callback`,
|