@shopify/shopify-app-remix 2.1.0 → 2.3.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/authenticate/admin/authenticate.js +119 -501
- package/build/cjs/server/authenticate/admin/helpers/create-admin-api-context.js +18 -0
- package/build/cjs/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.js +27 -0
- package/build/cjs/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js +25 -0
- package/build/cjs/server/authenticate/admin/helpers/redirect-to-bounce-page.js +22 -0
- package/build/cjs/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.js +22 -0
- package/build/cjs/server/authenticate/admin/helpers/trigger-after-auth-hook.js +21 -0
- package/build/cjs/server/authenticate/admin/helpers/validate-shop-and-host-params.js +32 -0
- package/build/cjs/server/authenticate/admin/strategies/auth-code-flow.js +307 -0
- package/build/cjs/server/authenticate/helpers/get-session-token-header.js +6 -0
- package/build/cjs/server/authenticate/helpers/reject-bot-request.js +2 -2
- package/build/cjs/server/authenticate/public/checkout/authenticate.js +1 -1
- package/build/cjs/server/authenticate/webhooks/authenticate.js +14 -6
- package/build/cjs/server/clients/admin/graphql.js +7 -16
- package/build/cjs/server/clients/storefront/factory.js +5 -12
- package/build/cjs/server/shopify-app.js +13 -6
- package/build/cjs/server/version.js +1 -1
- package/build/ts/server/__test-helpers/expect-admin-api-client.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/expect-admin-api-client.js +2 -2
- package/build/ts/server/__test-helpers/expect-admin-api-client.js.map +1 -1
- package/build/ts/server/__test-helpers/expect-storefront-api-client.js +1 -1
- package/build/ts/server/__test-helpers/expect-storefront-api-client.js.map +1 -1
- package/build/ts/server/__test-helpers/request-mock.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/request-mock.js +1 -1
- package/build/ts/server/__test-helpers/request-mock.js.map +1 -1
- package/build/ts/server/__test-helpers/test-config.d.ts +36 -7
- package/build/ts/server/__test-helpers/test-config.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/test-config.js +34 -6
- package/build/ts/server/__test-helpers/test-config.js.map +1 -1
- package/build/ts/server/authenticate/admin/authenticate.d.ts +8 -25
- package/build/ts/server/authenticate/admin/authenticate.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/authenticate.js +124 -488
- package/build/ts/server/authenticate/admin/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/admin/billing/types.d.ts +11 -3
- package/build/ts/server/authenticate/admin/billing/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts +5 -0
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js +16 -0
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.d.ts +3 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.js +25 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.js.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.d.ts +3 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js +23 -0
- package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/index.d.ts +7 -0
- package/build/ts/server/authenticate/admin/helpers/index.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/index.js +7 -0
- package/build/ts/server/authenticate/admin/helpers/index.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.d.ts +3 -0
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js +16 -0
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.d.ts +3 -0
- package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.js +32 -0
- package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.js.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts +4 -0
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js +28 -0
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.d.ts +3 -0
- package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.js +26 -0
- package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.js.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts +22 -0
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js +321 -0
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/types.d.ts +11 -0
- package/build/ts/server/authenticate/admin/strategies/types.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/types.js +3 -0
- package/build/ts/server/authenticate/admin/strategies/types.js.map +1 -0
- package/build/ts/server/authenticate/admin/types.d.ts +4 -0
- package/build/ts/server/authenticate/admin/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/helpers/get-session-token-header.d.ts +1 -0
- package/build/ts/server/authenticate/helpers/get-session-token-header.d.ts.map +1 -1
- package/build/ts/server/authenticate/helpers/get-session-token-header.js +7 -1
- package/build/ts/server/authenticate/helpers/get-session-token-header.js.map +1 -1
- package/build/ts/server/authenticate/helpers/reject-bot-request.d.ts +1 -1
- package/build/ts/server/authenticate/helpers/reject-bot-request.d.ts.map +1 -1
- package/build/ts/server/authenticate/helpers/reject-bot-request.js +3 -3
- package/build/ts/server/authenticate/helpers/reject-bot-request.js.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.js +2 -2
- package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.js.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/types.d.ts +6 -3
- package/build/ts/server/authenticate/public/appProxy/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/public/checkout/authenticate.js +1 -1
- package/build/ts/server/authenticate/public/checkout/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/webhooks/authenticate.d.ts.map +1 -1
- package/build/ts/server/authenticate/webhooks/authenticate.js +12 -2
- package/build/ts/server/authenticate/webhooks/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/webhooks/types.d.ts +12 -5
- package/build/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
- package/build/ts/server/clients/admin/graphql.d.ts +3 -3
- package/build/ts/server/clients/admin/graphql.d.ts.map +1 -1
- package/build/ts/server/clients/admin/graphql.js +6 -9
- package/build/ts/server/clients/admin/graphql.js.map +1 -1
- package/build/ts/server/clients/admin/types.d.ts +53 -11
- package/build/ts/server/clients/admin/types.d.ts.map +1 -1
- package/build/ts/server/clients/storefront/factory.d.ts.map +1 -1
- package/build/ts/server/clients/storefront/factory.js +5 -8
- package/build/ts/server/clients/storefront/factory.js.map +1 -1
- package/build/ts/server/clients/storefront/types.d.ts +6 -5
- package/build/ts/server/clients/storefront/types.d.ts.map +1 -1
- package/build/ts/server/clients/types.d.ts +4 -7
- package/build/ts/server/clients/types.d.ts.map +1 -1
- package/build/ts/server/config-types.d.ts +5 -5
- package/build/ts/server/config-types.d.ts.map +1 -1
- package/build/ts/server/future/flags.d.ts.map +1 -1
- package/build/ts/server/shopify-app.d.ts +1 -0
- package/build/ts/server/shopify-app.d.ts.map +1 -1
- package/build/ts/server/shopify-app.js +6 -3
- package/build/ts/server/shopify-app.js.map +1 -1
- package/build/ts/server/types.d.ts +3 -2
- package/build/ts/server/types.d.ts.map +1 -1
- package/build/ts/server/version.d.ts +1 -1
- package/build/ts/server/version.js +1 -1
- package/package.json +12 -10
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var serverRuntime = require('@remix-run/server-runtime');
|
|
6
|
-
var shopifyApi = require('@shopify/shopify-api');
|
|
7
|
-
var factory = require('../../clients/admin/factory.js');
|
|
8
5
|
var ensureCorsHeaders = require('../helpers/ensure-cors-headers.js');
|
|
9
6
|
var validateSessionToken = require('../helpers/validate-session-token.js');
|
|
10
7
|
var getSessionTokenHeader = require('../helpers/get-session-token-header.js');
|
|
@@ -13,539 +10,160 @@ var respondToOptionsRequest = require('../helpers/respond-to-options-request.js'
|
|
|
13
10
|
var cancel = require('./billing/cancel.js');
|
|
14
11
|
var require$1 = require('./billing/require.js');
|
|
15
12
|
var request = require('./billing/request.js');
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
|
|
13
|
+
var createAdminApiContext = require('./helpers/create-admin-api-context.js');
|
|
14
|
+
var ensureAppIsEmbeddedIfRequired = require('./helpers/ensure-app-is-embedded-if-required.js');
|
|
15
|
+
var ensureSessionTokenSearchParamIfRequired = require('./helpers/ensure-session-token-search-param-if-required.js');
|
|
16
|
+
require('@shopify/shopify-api');
|
|
17
|
+
require('@remix-run/server-runtime');
|
|
20
18
|
var redirect = require('./helpers/redirect.js');
|
|
21
19
|
var renderAppBridge = require('./helpers/render-app-bridge.js');
|
|
20
|
+
var validateShopAndHostParams = require('./helpers/validate-shop-and-host-params.js');
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
function authStrategyFactory({
|
|
23
|
+
strategy,
|
|
24
|
+
...params
|
|
25
|
+
}) {
|
|
26
|
+
const {
|
|
26
27
|
api,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
api,
|
|
40
|
-
logger,
|
|
41
|
-
config
|
|
42
|
-
} = this;
|
|
43
|
-
rejectBotRequest.rejectBotRequest({
|
|
44
|
-
api,
|
|
45
|
-
logger,
|
|
46
|
-
config
|
|
47
|
-
}, request);
|
|
48
|
-
respondToOptionsRequest.respondToOptionsRequest({
|
|
49
|
-
api,
|
|
50
|
-
logger,
|
|
51
|
-
config
|
|
52
|
-
}, request);
|
|
53
|
-
const cors = ensureCorsHeaders.ensureCORSHeadersFactory({
|
|
54
|
-
api,
|
|
55
|
-
logger,
|
|
56
|
-
config
|
|
57
|
-
}, request);
|
|
58
|
-
let sessionContext;
|
|
59
|
-
try {
|
|
60
|
-
sessionContext = await this.authenticateAndGetSessionContext(request);
|
|
61
|
-
} catch (errorOrResponse) {
|
|
62
|
-
if (errorOrResponse instanceof Response) {
|
|
63
|
-
cors(errorOrResponse);
|
|
64
|
-
}
|
|
65
|
-
throw errorOrResponse;
|
|
66
|
-
}
|
|
67
|
-
const context = {
|
|
68
|
-
admin: this.createAdminApiContext(request, sessionContext.session),
|
|
69
|
-
billing: this.createBillingContext(request, sessionContext.session),
|
|
70
|
-
session: sessionContext.session,
|
|
71
|
-
cors
|
|
72
|
-
};
|
|
73
|
-
if (config.isEmbeddedApp) {
|
|
74
|
-
return {
|
|
75
|
-
...context,
|
|
76
|
-
sessionToken: sessionContext.token,
|
|
77
|
-
redirect: redirect.redirectFactory({
|
|
78
|
-
api,
|
|
79
|
-
config,
|
|
80
|
-
logger
|
|
81
|
-
}, request)
|
|
82
|
-
};
|
|
83
|
-
} else {
|
|
84
|
-
return context;
|
|
28
|
+
logger,
|
|
29
|
+
config
|
|
30
|
+
} = params;
|
|
31
|
+
async function respondToBouncePageRequest(request) {
|
|
32
|
+
const url = new URL(request.url);
|
|
33
|
+
if (url.pathname === config.auth.patchSessionTokenPath) {
|
|
34
|
+
logger.debug('Rendering bounce page');
|
|
35
|
+
throw renderAppBridge.renderAppBridge({
|
|
36
|
+
config,
|
|
37
|
+
logger,
|
|
38
|
+
api
|
|
39
|
+
}, request);
|
|
85
40
|
}
|
|
86
41
|
}
|
|
87
|
-
async
|
|
88
|
-
const {
|
|
89
|
-
api,
|
|
90
|
-
logger,
|
|
91
|
-
config
|
|
92
|
-
} = this;
|
|
93
|
-
const params = {
|
|
94
|
-
api,
|
|
95
|
-
logger,
|
|
96
|
-
config
|
|
97
|
-
};
|
|
42
|
+
async function respondToExitIframeRequest(request) {
|
|
98
43
|
const url = new URL(request.url);
|
|
99
|
-
|
|
100
|
-
const isExitIframe = url.pathname === config.auth.exitIframePath;
|
|
101
|
-
const isAuthRequest = url.pathname === config.auth.path;
|
|
102
|
-
const isAuthCallbackRequest = url.pathname === config.auth.callbackPath;
|
|
103
|
-
const sessionTokenHeader = getSessionTokenHeader.getSessionTokenHeader(request);
|
|
104
|
-
logger.info('Authenticating admin request');
|
|
105
|
-
if (isPatchSessionToken) {
|
|
106
|
-
logger.debug('Rendering bounce page');
|
|
107
|
-
throw renderAppBridge.renderAppBridge(params, request);
|
|
108
|
-
} else if (isExitIframe) {
|
|
44
|
+
if (url.pathname === config.auth.exitIframePath) {
|
|
109
45
|
const destination = url.searchParams.get('exitIframe');
|
|
110
46
|
logger.debug('Rendering exit iframe page', {
|
|
111
47
|
destination
|
|
112
48
|
});
|
|
113
|
-
throw renderAppBridge.renderAppBridge(
|
|
49
|
+
throw renderAppBridge.renderAppBridge({
|
|
50
|
+
config,
|
|
51
|
+
logger,
|
|
52
|
+
api
|
|
53
|
+
}, request, {
|
|
114
54
|
url: destination
|
|
115
55
|
});
|
|
116
|
-
} else if (isAuthCallbackRequest) {
|
|
117
|
-
throw await this.handleAuthCallbackRequest(request);
|
|
118
|
-
} else if (isAuthRequest) {
|
|
119
|
-
throw await this.handleAuthBeginRequest(request);
|
|
120
|
-
} else if (sessionTokenHeader) {
|
|
121
|
-
const sessionToken = await validateSessionToken.validateSessionToken({
|
|
122
|
-
api,
|
|
123
|
-
logger,
|
|
124
|
-
config
|
|
125
|
-
}, sessionTokenHeader);
|
|
126
|
-
return this.validateAuthenticatedSession(request, sessionToken);
|
|
127
|
-
} else {
|
|
128
|
-
await this.validateUrlParams(request);
|
|
129
|
-
await this.ensureInstalledOnShop(request);
|
|
130
|
-
await this.ensureAppIsEmbeddedIfRequired(request);
|
|
131
|
-
await this.ensureSessionTokenSearchParamIfRequired(request);
|
|
132
|
-
return this.ensureSessionExists(request);
|
|
133
56
|
}
|
|
134
57
|
}
|
|
135
|
-
|
|
136
|
-
const {
|
|
137
|
-
|
|
138
|
-
config,
|
|
139
|
-
logger
|
|
140
|
-
} = this;
|
|
141
|
-
logger.info('Handling OAuth begin request');
|
|
142
|
-
const shop = this.ensureValidShopParam(request);
|
|
143
|
-
logger.debug('OAuth request contained valid shop', {
|
|
144
|
-
shop
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// If we're loading from an iframe, we need to break out of it
|
|
148
|
-
if (config.isEmbeddedApp && request.headers.get('Sec-Fetch-Dest') === 'iframe') {
|
|
149
|
-
logger.debug('Auth request in iframe detected, exiting iframe', {
|
|
150
|
-
shop
|
|
151
|
-
});
|
|
152
|
-
throw redirectWithExitiframe.redirectWithExitIframe({
|
|
58
|
+
function createContext(request$1, session, sessionToken) {
|
|
59
|
+
const context = {
|
|
60
|
+
admin: createAdminApiContext.createAdminApiContext(request$1, session, {
|
|
153
61
|
api,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
},
|
|
62
|
+
logger,
|
|
63
|
+
config
|
|
64
|
+
}),
|
|
65
|
+
billing: {
|
|
66
|
+
require: require$1.requireBillingFactory(params, request$1, session),
|
|
67
|
+
request: request.requestBillingFactory(params, request$1, session),
|
|
68
|
+
cancel: cancel.cancelBillingFactory(params, request$1, session)
|
|
69
|
+
},
|
|
70
|
+
session,
|
|
71
|
+
cors: ensureCorsHeaders.ensureCORSHeadersFactory(params, request$1)
|
|
72
|
+
};
|
|
73
|
+
if (config.isEmbeddedApp) {
|
|
74
|
+
return {
|
|
75
|
+
...context,
|
|
76
|
+
sessionToken,
|
|
77
|
+
redirect: redirect.redirectFactory(params, request$1)
|
|
78
|
+
};
|
|
157
79
|
} else {
|
|
158
|
-
|
|
159
|
-
api,
|
|
160
|
-
config,
|
|
161
|
-
logger
|
|
162
|
-
}, request, false, shop);
|
|
80
|
+
return context;
|
|
163
81
|
}
|
|
164
82
|
}
|
|
165
|
-
async
|
|
166
|
-
const {
|
|
167
|
-
api,
|
|
168
|
-
config,
|
|
169
|
-
logger
|
|
170
|
-
} = this;
|
|
171
|
-
logger.info('Handling OAuth callback request');
|
|
172
|
-
const shop = this.ensureValidShopParam(request);
|
|
83
|
+
return async function authenticateAdmin(request) {
|
|
173
84
|
try {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
});
|
|
180
|
-
await config.sessionStorage.storeSession(session);
|
|
181
|
-
if (config.useOnlineTokens && !session.isOnline) {
|
|
182
|
-
logger.info('Requesting online access token for offline session');
|
|
183
|
-
await beginAuth.beginAuth({
|
|
184
|
-
api,
|
|
185
|
-
config,
|
|
186
|
-
logger
|
|
187
|
-
}, request, true, shop);
|
|
188
|
-
}
|
|
189
|
-
if (config.hooks.afterAuth) {
|
|
190
|
-
logger.info('Running afterAuth hook');
|
|
191
|
-
await config.hooks.afterAuth({
|
|
192
|
-
session,
|
|
193
|
-
admin: this.createAdminApiContext(request, session)
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
throw await this.redirectToShopifyOrAppRoot(request, responseHeaders);
|
|
197
|
-
} catch (error) {
|
|
198
|
-
if (error instanceof Response) {
|
|
199
|
-
throw error;
|
|
200
|
-
}
|
|
201
|
-
logger.error('Error during OAuth callback', {
|
|
202
|
-
error: error.message
|
|
203
|
-
});
|
|
204
|
-
if (error instanceof shopifyApi.CookieNotFound) {
|
|
205
|
-
throw await this.handleAuthBeginRequest(request);
|
|
206
|
-
} else if (error instanceof shopifyApi.InvalidHmacError || error instanceof shopifyApi.InvalidOAuthError) {
|
|
207
|
-
throw new Response(undefined, {
|
|
208
|
-
status: 400,
|
|
209
|
-
statusText: 'Invalid OAuth Request'
|
|
210
|
-
});
|
|
211
|
-
} else {
|
|
212
|
-
throw new Response(undefined, {
|
|
213
|
-
status: 500,
|
|
214
|
-
statusText: 'Internal Server Error'
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
async validateUrlParams(request) {
|
|
220
|
-
const {
|
|
221
|
-
api,
|
|
222
|
-
config,
|
|
223
|
-
logger
|
|
224
|
-
} = this;
|
|
225
|
-
if (config.isEmbeddedApp) {
|
|
226
|
-
const url = new URL(request.url);
|
|
227
|
-
const shop = api.utils.sanitizeShop(url.searchParams.get('shop'));
|
|
228
|
-
if (!shop) {
|
|
229
|
-
logger.debug('Missing or invalid shop, redirecting to login path', {
|
|
230
|
-
shop
|
|
231
|
-
});
|
|
232
|
-
throw serverRuntime.redirect(config.auth.loginPath);
|
|
233
|
-
}
|
|
234
|
-
const host = api.utils.sanitizeHost(url.searchParams.get('host'));
|
|
235
|
-
if (!host) {
|
|
236
|
-
logger.debug('Invalid host, redirecting to login path', {
|
|
237
|
-
host: url.searchParams.get('host')
|
|
238
|
-
});
|
|
239
|
-
throw serverRuntime.redirect(config.auth.loginPath);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
async ensureInstalledOnShop(request) {
|
|
244
|
-
const {
|
|
245
|
-
api,
|
|
246
|
-
config,
|
|
247
|
-
logger
|
|
248
|
-
} = this;
|
|
249
|
-
const url = new URL(request.url);
|
|
250
|
-
let shop = url.searchParams.get('shop');
|
|
251
|
-
const isEmbedded = url.searchParams.get('embedded') === '1';
|
|
85
|
+
rejectBotRequest.respondToBotRequest(params, request);
|
|
86
|
+
respondToOptionsRequest.respondToOptionsRequest(params, request);
|
|
87
|
+
await respondToBouncePageRequest(request);
|
|
88
|
+
await respondToExitIframeRequest(request);
|
|
89
|
+
await strategy.respondToOAuthRequests(request);
|
|
252
90
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
rawRequest: request
|
|
260
|
-
});
|
|
261
|
-
if (!offlineId) {
|
|
262
|
-
logger.info("Could not find a shop, can't authenticate request");
|
|
263
|
-
throw new Response(undefined, {
|
|
264
|
-
status: 400,
|
|
265
|
-
statusText: 'Bad Request'
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
const offlineSession = await config.sessionStorage.loadSession(offlineId);
|
|
269
|
-
if (!offlineSession) {
|
|
270
|
-
logger.info("Shop hasn't installed app yet, redirecting to OAuth", {
|
|
271
|
-
shop
|
|
272
|
-
});
|
|
273
|
-
if (isEmbedded) {
|
|
274
|
-
redirectWithExitiframe.redirectWithExitIframe({
|
|
275
|
-
api,
|
|
276
|
-
config,
|
|
277
|
-
logger
|
|
278
|
-
}, request, shop);
|
|
279
|
-
} else {
|
|
280
|
-
throw await beginAuth.beginAuth({
|
|
281
|
-
api,
|
|
282
|
-
config,
|
|
283
|
-
logger
|
|
284
|
-
}, request, false, shop);
|
|
91
|
+
// If this is a valid request, but it doesn't have a session token header, this is a document request. We need to
|
|
92
|
+
// ensure we're embedded if needed and we have the information needed to load the session.
|
|
93
|
+
if (!getSessionTokenHeader.getSessionTokenHeader(request)) {
|
|
94
|
+
validateShopAndHostParams.validateShopAndHostParams(params, request);
|
|
95
|
+
await ensureAppIsEmbeddedIfRequired.ensureAppIsEmbeddedIfRequired(params, request);
|
|
96
|
+
await ensureSessionTokenSearchParamIfRequired.ensureSessionTokenSearchParamIfRequired(params, request);
|
|
285
97
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
logger.debug('Offline session is still valid, embedding app', {
|
|
295
|
-
shop
|
|
296
|
-
});
|
|
297
|
-
} catch (error) {
|
|
298
|
-
if (error instanceof shopifyApi.HttpResponseError) {
|
|
299
|
-
if (error.response.code === 401) {
|
|
300
|
-
logger.info('Shop session is no longer valid, redirecting to OAuth', {
|
|
301
|
-
shop
|
|
302
|
-
});
|
|
303
|
-
throw await beginAuth.beginAuth({
|
|
304
|
-
api,
|
|
305
|
-
config,
|
|
306
|
-
logger
|
|
307
|
-
}, request, false, shop);
|
|
308
|
-
} else {
|
|
309
|
-
const message = JSON.stringify(error.response.body, null, 2);
|
|
310
|
-
logger.error(`Unexpected error during session validation: ${message}`, {
|
|
311
|
-
shop
|
|
312
|
-
});
|
|
313
|
-
throw new Response(undefined, {
|
|
314
|
-
status: error.response.code,
|
|
315
|
-
statusText: error.response.statusText
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
} else if (error instanceof shopifyApi.GraphqlQueryError) {
|
|
319
|
-
const context = {
|
|
320
|
-
shop
|
|
321
|
-
};
|
|
322
|
-
if (error.response) {
|
|
323
|
-
context.response = JSON.stringify(error.response);
|
|
324
|
-
}
|
|
325
|
-
logger.error(`Unexpected error during session validation: ${error.message}`, context);
|
|
326
|
-
throw new Response(undefined, {
|
|
327
|
-
status: 500,
|
|
328
|
-
statusText: 'Internal Server Error'
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
async testSession(session) {
|
|
335
|
-
const {
|
|
336
|
-
api
|
|
337
|
-
} = this;
|
|
338
|
-
const client = new api.clients.Graphql({
|
|
339
|
-
session
|
|
340
|
-
});
|
|
341
|
-
await client.query({
|
|
342
|
-
data: `#graphql
|
|
343
|
-
query shopifyAppShopName {
|
|
344
|
-
shop {
|
|
345
|
-
name
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
`
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
ensureValidShopParam(request) {
|
|
352
|
-
const url = new URL(request.url);
|
|
353
|
-
const {
|
|
354
|
-
api
|
|
355
|
-
} = this;
|
|
356
|
-
const shop = api.utils.sanitizeShop(url.searchParams.get('shop'));
|
|
357
|
-
if (!shop) {
|
|
358
|
-
throw new Response('Shop param is invalid', {
|
|
359
|
-
status: 400
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
return shop;
|
|
363
|
-
}
|
|
364
|
-
async ensureAppIsEmbeddedIfRequired(request) {
|
|
365
|
-
const {
|
|
366
|
-
api,
|
|
367
|
-
logger
|
|
368
|
-
} = this;
|
|
369
|
-
const url = new URL(request.url);
|
|
370
|
-
const shop = url.searchParams.get('shop');
|
|
371
|
-
if (api.config.isEmbeddedApp && url.searchParams.get('embedded') !== '1') {
|
|
372
|
-
logger.debug('App is not embedded, redirecting to Shopify', {
|
|
373
|
-
shop
|
|
374
|
-
});
|
|
375
|
-
await this.redirectToShopifyOrAppRoot(request);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
async ensureSessionTokenSearchParamIfRequired(request) {
|
|
379
|
-
const {
|
|
380
|
-
api,
|
|
381
|
-
logger
|
|
382
|
-
} = this;
|
|
383
|
-
const url = new URL(request.url);
|
|
384
|
-
const shop = url.searchParams.get('shop');
|
|
385
|
-
const searchParamSessionToken = url.searchParams.get(SESSION_TOKEN_PARAM);
|
|
386
|
-
if (api.config.isEmbeddedApp && !searchParamSessionToken) {
|
|
387
|
-
logger.debug('Missing session token in search params, going to bounce page', {
|
|
388
|
-
shop
|
|
389
|
-
});
|
|
390
|
-
this.redirectToBouncePage(url);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
async ensureSessionExists(request) {
|
|
394
|
-
const {
|
|
395
|
-
api,
|
|
396
|
-
config,
|
|
397
|
-
logger
|
|
398
|
-
} = this;
|
|
399
|
-
const url = new URL(request.url);
|
|
400
|
-
const shop = url.searchParams.get('shop');
|
|
401
|
-
const searchParamSessionToken = url.searchParams.get(SESSION_TOKEN_PARAM);
|
|
402
|
-
if (api.config.isEmbeddedApp) {
|
|
403
|
-
logger.debug('Session token is present in query params, validating session', {
|
|
404
|
-
shop
|
|
98
|
+
logger.info('Authenticating admin request');
|
|
99
|
+
const {
|
|
100
|
+
payload,
|
|
101
|
+
shop,
|
|
102
|
+
sessionId
|
|
103
|
+
} = await getSessionTokenContext(params, request);
|
|
104
|
+
logger.debug('Loading session from storage', {
|
|
105
|
+
sessionId
|
|
405
106
|
});
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return this.validateAuthenticatedSession(request, sessionToken);
|
|
412
|
-
} else {
|
|
413
|
-
// eslint-disable-next-line no-warning-comments
|
|
414
|
-
// TODO move this check into loadSession once we add support for it in the library
|
|
415
|
-
// https://github.com/orgs/Shopify/projects/6899/views/1?pane=issue&itemId=28378114
|
|
416
|
-
const sessionId = await api.session.getCurrentId({
|
|
417
|
-
isOnline: config.useOnlineTokens,
|
|
418
|
-
rawRequest: request
|
|
107
|
+
const existingSession = sessionId ? await config.sessionStorage.loadSession(sessionId) : undefined;
|
|
108
|
+
const session = await strategy.authenticate(request, existingSession, shop);
|
|
109
|
+
logger.debug('Request is valid, loaded session from session token', {
|
|
110
|
+
shop: session.shop,
|
|
111
|
+
isOnline: session.isOnline
|
|
419
112
|
});
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
throw await beginAuth.beginAuth({
|
|
425
|
-
api,
|
|
426
|
-
config,
|
|
427
|
-
logger
|
|
428
|
-
}, request, false, shop);
|
|
113
|
+
return createContext(request, session, payload);
|
|
114
|
+
} catch (errorOrResponse) {
|
|
115
|
+
if (errorOrResponse instanceof Response) {
|
|
116
|
+
ensureCorsHeaders.ensureCORSHeadersFactory(params, request)(errorOrResponse);
|
|
429
117
|
}
|
|
430
|
-
|
|
431
|
-
session: await this.loadSession(request, shop, sessionId)
|
|
432
|
-
};
|
|
118
|
+
throw errorOrResponse;
|
|
433
119
|
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async function getSessionTokenContext(params, request) {
|
|
123
|
+
const {
|
|
124
|
+
api,
|
|
125
|
+
config,
|
|
126
|
+
logger
|
|
127
|
+
} = params;
|
|
128
|
+
const headerSessionToken = getSessionTokenHeader.getSessionTokenHeader(request);
|
|
129
|
+
const searchParamSessionToken = getSessionTokenHeader.getSessionTokenFromUrlParam(request);
|
|
130
|
+
const sessionToken = headerSessionToken || searchParamSessionToken;
|
|
131
|
+
logger.debug('Attempting to authenticate session token', {
|
|
132
|
+
sessionToken: {
|
|
133
|
+
header: headerSessionToken,
|
|
134
|
+
search: searchParamSessionToken
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
if (config.isEmbeddedApp) {
|
|
138
|
+
const payload = await validateSessionToken.validateSessionToken({
|
|
437
139
|
config,
|
|
438
140
|
logger,
|
|
439
141
|
api
|
|
440
|
-
}
|
|
142
|
+
}, sessionToken);
|
|
441
143
|
const dest = new URL(payload.dest);
|
|
442
144
|
const shop = dest.hostname;
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
shop
|
|
145
|
+
logger.debug('Session token is valid', {
|
|
146
|
+
shop,
|
|
147
|
+
payload
|
|
447
148
|
});
|
|
149
|
+
const sessionId = config.useOnlineTokens ? api.session.getJwtSessionId(shop, payload.sub) : api.session.getOfflineId(shop);
|
|
448
150
|
return {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
async loadSession(request, shop, sessionId) {
|
|
454
|
-
const {
|
|
455
|
-
api,
|
|
456
|
-
config,
|
|
457
|
-
logger
|
|
458
|
-
} = this;
|
|
459
|
-
logger.debug('Loading session from storage', {
|
|
151
|
+
shop,
|
|
152
|
+
payload,
|
|
460
153
|
sessionId
|
|
461
|
-
});
|
|
462
|
-
const session = await config.sessionStorage.loadSession(sessionId);
|
|
463
|
-
if (!session) {
|
|
464
|
-
logger.debug('No session found, redirecting to OAuth', {
|
|
465
|
-
shop
|
|
466
|
-
});
|
|
467
|
-
await redirectToAuthPage.redirectToAuthPage({
|
|
468
|
-
api,
|
|
469
|
-
config,
|
|
470
|
-
logger
|
|
471
|
-
}, request, shop);
|
|
472
|
-
} else if (!session.isActive(config.scopes)) {
|
|
473
|
-
logger.debug('Found a session, but it has expired, redirecting to OAuth', {
|
|
474
|
-
shop
|
|
475
|
-
});
|
|
476
|
-
await redirectToAuthPage.redirectToAuthPage({
|
|
477
|
-
api,
|
|
478
|
-
config,
|
|
479
|
-
logger
|
|
480
|
-
}, request, shop);
|
|
481
|
-
}
|
|
482
|
-
return session;
|
|
483
|
-
}
|
|
484
|
-
async redirectToShopifyOrAppRoot(request, responseHeaders) {
|
|
485
|
-
const {
|
|
486
|
-
api
|
|
487
|
-
} = this;
|
|
488
|
-
const url = new URL(request.url);
|
|
489
|
-
const host = api.utils.sanitizeHost(url.searchParams.get('host'));
|
|
490
|
-
const shop = api.utils.sanitizeShop(url.searchParams.get('shop'));
|
|
491
|
-
const redirectUrl = api.config.isEmbeddedApp ? await api.auth.getEmbeddedAppUrl({
|
|
492
|
-
rawRequest: request
|
|
493
|
-
}) : `/?shop=${shop}&host=${encodeURIComponent(host)}`;
|
|
494
|
-
throw serverRuntime.redirect(redirectUrl, {
|
|
495
|
-
headers: responseHeaders
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
redirectToBouncePage(url) {
|
|
499
|
-
const {
|
|
500
|
-
config
|
|
501
|
-
} = this;
|
|
502
|
-
|
|
503
|
-
// Make sure we always point to the configured app URL so it also works behind reverse proxies (that alter the Host
|
|
504
|
-
// header).
|
|
505
|
-
url.searchParams.set('shopify-reload', `${config.appUrl}${url.pathname}${url.search}`);
|
|
506
|
-
|
|
507
|
-
// eslint-disable-next-line no-warning-comments
|
|
508
|
-
// TODO Make sure this works on chrome without a tunnel (weird HTTPS redirect issue)
|
|
509
|
-
// https://github.com/orgs/Shopify/projects/6899/views/1?pane=issue&itemId=28376650
|
|
510
|
-
throw serverRuntime.redirect(`${config.auth.patchSessionTokenPath}${url.search}`);
|
|
511
|
-
}
|
|
512
|
-
createBillingContext(request$1, session) {
|
|
513
|
-
const {
|
|
514
|
-
api,
|
|
515
|
-
logger,
|
|
516
|
-
config
|
|
517
|
-
} = this;
|
|
518
|
-
return {
|
|
519
|
-
require: require$1.requireBillingFactory({
|
|
520
|
-
api,
|
|
521
|
-
logger,
|
|
522
|
-
config
|
|
523
|
-
}, request$1, session),
|
|
524
|
-
request: request.requestBillingFactory({
|
|
525
|
-
api,
|
|
526
|
-
logger,
|
|
527
|
-
config
|
|
528
|
-
}, request$1, session),
|
|
529
|
-
cancel: cancel.cancelBillingFactory({
|
|
530
|
-
api,
|
|
531
|
-
logger,
|
|
532
|
-
config
|
|
533
|
-
}, request$1, session)
|
|
534
154
|
};
|
|
535
155
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
});
|
|
548
|
-
}
|
|
156
|
+
const url = new URL(request.url);
|
|
157
|
+
const shop = url.searchParams.get('shop');
|
|
158
|
+
const sessionId = await api.session.getCurrentId({
|
|
159
|
+
isOnline: config.useOnlineTokens,
|
|
160
|
+
rawRequest: request
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
shop,
|
|
164
|
+
sessionId,
|
|
165
|
+
payload: undefined
|
|
166
|
+
};
|
|
549
167
|
}
|
|
550
168
|
|
|
551
|
-
exports.
|
|
169
|
+
exports.authStrategyFactory = authStrategyFactory;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var factory = require('../../../clients/admin/factory.js');
|
|
6
|
+
var handleClientError = require('./handle-client-error.js');
|
|
7
|
+
|
|
8
|
+
function createAdminApiContext(request, session, params) {
|
|
9
|
+
return factory.adminClientFactory({
|
|
10
|
+
session,
|
|
11
|
+
params,
|
|
12
|
+
handleClientError: handleClientError.handleClientErrorFactory({
|
|
13
|
+
request
|
|
14
|
+
})
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.createAdminApiContext = createAdminApiContext;
|