@shopify/shopify-app-remix 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/build/cjs/server/authenticate/admin/authenticate.js +119 -501
  2. package/build/cjs/server/authenticate/admin/billing/cancel.js +1 -0
  3. package/build/cjs/server/authenticate/admin/billing/request.js +1 -0
  4. package/build/cjs/server/authenticate/admin/billing/require.js +1 -0
  5. package/build/cjs/server/authenticate/admin/helpers/create-admin-api-context.js +18 -0
  6. package/build/cjs/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.js +27 -0
  7. package/build/cjs/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js +25 -0
  8. package/build/cjs/server/authenticate/admin/helpers/redirect-to-bounce-page.js +22 -0
  9. package/build/cjs/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.js +22 -0
  10. package/build/cjs/server/authenticate/admin/helpers/trigger-after-auth-hook.js +21 -0
  11. package/build/cjs/server/authenticate/admin/helpers/validate-shop-and-host-params.js +32 -0
  12. package/build/cjs/server/authenticate/admin/strategies/auth-code-flow.js +310 -0
  13. package/build/cjs/server/authenticate/helpers/get-session-token-header.js +6 -0
  14. package/build/cjs/server/authenticate/helpers/reject-bot-request.js +2 -2
  15. package/build/cjs/server/authenticate/public/checkout/authenticate.js +1 -1
  16. package/build/cjs/server/authenticate/webhooks/authenticate.js +14 -5
  17. package/build/cjs/server/clients/admin/graphql.js +6 -8
  18. package/build/cjs/server/shopify-app.js +12 -6
  19. package/build/cjs/server/version.js +1 -1
  20. package/build/ts/server/__test-helpers/test-config.d.ts +36 -7
  21. package/build/ts/server/__test-helpers/test-config.d.ts.map +1 -1
  22. package/build/ts/server/__test-helpers/test-config.js +34 -6
  23. package/build/ts/server/__test-helpers/test-config.js.map +1 -1
  24. package/build/ts/server/authenticate/admin/authenticate.d.ts +8 -25
  25. package/build/ts/server/authenticate/admin/authenticate.d.ts.map +1 -1
  26. package/build/ts/server/authenticate/admin/authenticate.js +124 -488
  27. package/build/ts/server/authenticate/admin/authenticate.js.map +1 -1
  28. package/build/ts/server/authenticate/admin/billing/types.d.ts +11 -3
  29. package/build/ts/server/authenticate/admin/billing/types.d.ts.map +1 -1
  30. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts +5 -0
  31. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts.map +1 -0
  32. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js +16 -0
  33. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js.map +1 -0
  34. package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.d.ts +3 -0
  35. package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.d.ts.map +1 -0
  36. package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.js +25 -0
  37. package/build/ts/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.js.map +1 -0
  38. package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.d.ts +3 -0
  39. package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.d.ts.map +1 -0
  40. package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js +23 -0
  41. package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js.map +1 -0
  42. package/build/ts/server/authenticate/admin/helpers/index.d.ts +7 -0
  43. package/build/ts/server/authenticate/admin/helpers/index.d.ts.map +1 -1
  44. package/build/ts/server/authenticate/admin/helpers/index.js +7 -0
  45. package/build/ts/server/authenticate/admin/helpers/index.js.map +1 -1
  46. package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.d.ts +3 -0
  47. package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.d.ts.map +1 -0
  48. package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js +16 -0
  49. package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js.map +1 -0
  50. package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.d.ts +3 -0
  51. package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.d.ts.map +1 -0
  52. package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.js +32 -0
  53. package/build/ts/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.js.map +1 -0
  54. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts +4 -0
  55. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts.map +1 -0
  56. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js +28 -0
  57. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js.map +1 -0
  58. package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.d.ts +3 -0
  59. package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.d.ts.map +1 -0
  60. package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.js +26 -0
  61. package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.js.map +1 -0
  62. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts +22 -0
  63. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -0
  64. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js +323 -0
  65. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -0
  66. package/build/ts/server/authenticate/admin/strategies/types.d.ts +11 -0
  67. package/build/ts/server/authenticate/admin/strategies/types.d.ts.map +1 -0
  68. package/build/ts/server/authenticate/admin/strategies/types.js +3 -0
  69. package/build/ts/server/authenticate/admin/strategies/types.js.map +1 -0
  70. package/build/ts/server/authenticate/admin/types.d.ts +4 -0
  71. package/build/ts/server/authenticate/admin/types.d.ts.map +1 -1
  72. package/build/ts/server/authenticate/helpers/get-session-token-header.d.ts +1 -0
  73. package/build/ts/server/authenticate/helpers/get-session-token-header.d.ts.map +1 -1
  74. package/build/ts/server/authenticate/helpers/get-session-token-header.js +7 -1
  75. package/build/ts/server/authenticate/helpers/get-session-token-header.js.map +1 -1
  76. package/build/ts/server/authenticate/helpers/reject-bot-request.d.ts +1 -1
  77. package/build/ts/server/authenticate/helpers/reject-bot-request.d.ts.map +1 -1
  78. package/build/ts/server/authenticate/helpers/reject-bot-request.js +3 -3
  79. package/build/ts/server/authenticate/helpers/reject-bot-request.js.map +1 -1
  80. package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts.map +1 -1
  81. package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.js +2 -2
  82. package/build/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.js.map +1 -1
  83. package/build/ts/server/authenticate/public/appProxy/types.d.ts +6 -3
  84. package/build/ts/server/authenticate/public/appProxy/types.d.ts.map +1 -1
  85. package/build/ts/server/authenticate/public/checkout/authenticate.js +1 -1
  86. package/build/ts/server/authenticate/public/checkout/authenticate.js.map +1 -1
  87. package/build/ts/server/authenticate/webhooks/authenticate.d.ts.map +1 -1
  88. package/build/ts/server/authenticate/webhooks/authenticate.js +12 -2
  89. package/build/ts/server/authenticate/webhooks/authenticate.js.map +1 -1
  90. package/build/ts/server/clients/admin/graphql.js +2 -2
  91. package/build/ts/server/clients/admin/graphql.js.map +1 -1
  92. package/build/ts/server/clients/admin/types.d.ts +51 -10
  93. package/build/ts/server/clients/admin/types.d.ts.map +1 -1
  94. package/build/ts/server/clients/storefront/types.d.ts +4 -4
  95. package/build/ts/server/config-types.d.ts +5 -5
  96. package/build/ts/server/config-types.d.ts.map +1 -1
  97. package/build/ts/server/future/flags.d.ts.map +1 -1
  98. package/build/ts/server/shopify-app.d.ts.map +1 -1
  99. package/build/ts/server/shopify-app.js +5 -3
  100. package/build/ts/server/shopify-app.js.map +1 -1
  101. package/build/ts/server/types.d.ts +2 -2
  102. package/build/ts/server/version.d.ts +1 -1
  103. package/build/ts/server/version.js +1 -1
  104. package/package.json +4 -4
@@ -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;
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var redirectToShopifyOrAppRoot = require('./redirect-to-shopify-or-app-root.js');
6
+
7
+ const ensureAppIsEmbeddedIfRequired = async (params, request) => {
8
+ const {
9
+ api,
10
+ logger,
11
+ config
12
+ } = params;
13
+ const url = new URL(request.url);
14
+ const shop = url.searchParams.get('shop');
15
+ if (api.config.isEmbeddedApp && url.searchParams.get('embedded') !== '1') {
16
+ logger.debug('App is not embedded, redirecting to Shopify', {
17
+ shop
18
+ });
19
+ await redirectToShopifyOrAppRoot.redirectToShopifyOrAppRoot(request, {
20
+ api,
21
+ logger,
22
+ config
23
+ });
24
+ }
25
+ };
26
+
27
+ exports.ensureAppIsEmbeddedIfRequired = ensureAppIsEmbeddedIfRequired;
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var redirectToBouncePage = require('./redirect-to-bounce-page.js');
6
+
7
+ const SESSION_TOKEN_PARAM = 'id_token';
8
+ const ensureSessionTokenSearchParamIfRequired = async (params, request) => {
9
+ const {
10
+ api,
11
+ logger
12
+ } = params;
13
+ const url = new URL(request.url);
14
+ const shop = url.searchParams.get('shop');
15
+ const searchParamSessionToken = url.searchParams.get(SESSION_TOKEN_PARAM);
16
+ const isEmbedded = url.searchParams.get('embedded') === '1';
17
+ if (api.config.isEmbeddedApp && isEmbedded && !searchParamSessionToken) {
18
+ logger.debug('Missing session token in search params, going to bounce page', {
19
+ shop
20
+ });
21
+ redirectToBouncePage.redirectToBouncePage(params, url);
22
+ }
23
+ };
24
+
25
+ exports.ensureSessionTokenSearchParamIfRequired = ensureSessionTokenSearchParamIfRequired;
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var serverRuntime = require('@remix-run/server-runtime');
6
+
7
+ const redirectToBouncePage = (params, url) => {
8
+ const {
9
+ config
10
+ } = params;
11
+
12
+ // Make sure we always point to the configured app URL so it also works behind reverse proxies (that alter the Host
13
+ // header).
14
+ url.searchParams.set('shopify-reload', `${config.appUrl}${url.pathname}${url.search}`);
15
+
16
+ // eslint-disable-next-line no-warning-comments
17
+ // TODO Make sure this works on chrome without a tunnel (weird HTTPS redirect issue)
18
+ // https://github.com/orgs/Shopify/projects/6899/views/1?pane=issue&itemId=28376650
19
+ throw serverRuntime.redirect(`${config.auth.patchSessionTokenPath}${url.search}`);
20
+ };
21
+
22
+ exports.redirectToBouncePage = redirectToBouncePage;
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var serverRuntime = require('@remix-run/server-runtime');
6
+
7
+ async function redirectToShopifyOrAppRoot(request, params, responseHeaders) {
8
+ const {
9
+ api
10
+ } = params;
11
+ const url = new URL(request.url);
12
+ const host = api.utils.sanitizeHost(url.searchParams.get('host'));
13
+ const shop = api.utils.sanitizeShop(url.searchParams.get('shop'));
14
+ const redirectUrl = api.config.isEmbeddedApp ? await api.auth.getEmbeddedAppUrl({
15
+ rawRequest: request
16
+ }) : `/?shop=${shop}&host=${encodeURIComponent(host)}`;
17
+ throw serverRuntime.redirect(redirectUrl, {
18
+ headers: responseHeaders
19
+ });
20
+ }
21
+
22
+ exports.redirectToShopifyOrAppRoot = redirectToShopifyOrAppRoot;
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var createAdminApiContext = require('./create-admin-api-context.js');
6
+
7
+ async function triggerAfterAuthHook(params, session, request) {
8
+ const {
9
+ config,
10
+ logger
11
+ } = params;
12
+ if (config.hooks.afterAuth) {
13
+ logger.info('Running afterAuth hook');
14
+ await config.hooks.afterAuth({
15
+ session,
16
+ admin: createAdminApiContext.createAdminApiContext(request, session, params)
17
+ });
18
+ }
19
+ }
20
+
21
+ exports.triggerAfterAuthHook = triggerAfterAuthHook;
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var serverRuntime = require('@remix-run/server-runtime');
6
+
7
+ function validateShopAndHostParams(params, request) {
8
+ const {
9
+ api,
10
+ config,
11
+ logger
12
+ } = params;
13
+ if (config.isEmbeddedApp) {
14
+ const url = new URL(request.url);
15
+ const shop = api.utils.sanitizeShop(url.searchParams.get('shop'));
16
+ if (!shop) {
17
+ logger.debug('Missing or invalid shop, redirecting to login path', {
18
+ shop
19
+ });
20
+ throw serverRuntime.redirect(config.auth.loginPath);
21
+ }
22
+ const host = api.utils.sanitizeHost(url.searchParams.get('host'));
23
+ if (!host) {
24
+ logger.debug('Invalid host, redirecting to login path', {
25
+ host: url.searchParams.get('host')
26
+ });
27
+ throw serverRuntime.redirect(config.auth.loginPath);
28
+ }
29
+ }
30
+ }
31
+
32
+ exports.validateShopAndHostParams = validateShopAndHostParams;
@@ -0,0 +1,310 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var shopifyApi = require('@shopify/shopify-api');
6
+ var beginAuth = require('../helpers/begin-auth.js');
7
+ require('@shopify/shopify-api/runtime');
8
+ var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
9
+ var redirectToShopifyOrAppRoot = require('../helpers/redirect-to-shopify-or-app-root.js');
10
+ require('@remix-run/server-runtime');
11
+ var redirectWithExitiframe = require('../helpers/redirect-with-exitiframe.js');
12
+ var getSessionTokenHeader = require('../../helpers/get-session-token-header.js');
13
+ var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
14
+ var validateShopAndHostParams = require('../helpers/validate-shop-and-host-params.js');
15
+ require('isbot');
16
+
17
+ class AuthCodeFlowStrategy {
18
+ constructor({
19
+ api,
20
+ config,
21
+ logger
22
+ }) {
23
+ this.api = void 0;
24
+ this.config = void 0;
25
+ this.logger = void 0;
26
+ this.api = api;
27
+ this.config = config;
28
+ this.logger = logger;
29
+ }
30
+ async respondToOAuthRequests(request) {
31
+ const {
32
+ api,
33
+ config
34
+ } = this;
35
+ const url = new URL(request.url);
36
+ const isAuthRequest = url.pathname === config.auth.path;
37
+ const isAuthCallbackRequest = url.pathname === config.auth.callbackPath;
38
+ if (isAuthRequest || isAuthCallbackRequest) {
39
+ const shop = api.utils.sanitizeShop(url.searchParams.get('shop'));
40
+ if (!shop) throw new Response('Shop param is invalid', {
41
+ status: 400
42
+ });
43
+ if (isAuthRequest) {
44
+ throw await this.handleAuthBeginRequest(request, shop);
45
+ } else {
46
+ throw await this.handleAuthCallbackRequest(request, shop);
47
+ }
48
+ }
49
+ if (!getSessionTokenHeader.getSessionTokenHeader(request)) {
50
+ // This is a document request that doesn't contain a session token. We check if the app is installed.
51
+ // If the app isn't installed, we initiate the OAuth auth code flow.
52
+ // Requests with a header can only happen after the app is installed.
53
+ await this.ensureInstalledOnShop(request);
54
+ }
55
+ }
56
+ async authenticate(request, session, shop) {
57
+ const {
58
+ api,
59
+ config,
60
+ logger
61
+ } = this;
62
+ if (!session) {
63
+ logger.debug('No session found, redirecting to OAuth', {
64
+ shop
65
+ });
66
+ await redirectToAuthPage.redirectToAuthPage({
67
+ config,
68
+ logger,
69
+ api
70
+ }, request, shop);
71
+ } else if (!session.isActive(config.scopes)) {
72
+ logger.debug('Found a session, but it has expired, redirecting to OAuth', {
73
+ shop
74
+ });
75
+ await redirectToAuthPage.redirectToAuthPage({
76
+ config,
77
+ logger,
78
+ api
79
+ }, request, shop);
80
+ }
81
+ logger.debug('Found a valid session', {
82
+ shop
83
+ });
84
+ return session;
85
+ }
86
+ async ensureInstalledOnShop(request) {
87
+ const {
88
+ api,
89
+ config,
90
+ logger
91
+ } = this;
92
+ validateShopAndHostParams.validateShopAndHostParams({
93
+ api,
94
+ config,
95
+ logger
96
+ }, request);
97
+ const url = new URL(request.url);
98
+ let shop = url.searchParams.get('shop');
99
+
100
+ // Ensure app is installed
101
+ logger.debug('Ensuring app is installed on shop', {
102
+ shop
103
+ });
104
+ if (!(await this.hasValidOfflineId(request))) {
105
+ logger.info("Could not find a shop, can't authenticate request");
106
+ throw new Response(undefined, {
107
+ status: 400,
108
+ statusText: 'Bad Request'
109
+ });
110
+ }
111
+ const offlineSession = await this.getOfflineSession(request);
112
+ const isEmbedded = url.searchParams.get('embedded') === '1';
113
+ if (!offlineSession) {
114
+ logger.info("Shop hasn't installed app yet, redirecting to OAuth", {
115
+ shop
116
+ });
117
+ if (isEmbedded) {
118
+ redirectWithExitiframe.redirectWithExitIframe({
119
+ api,
120
+ config,
121
+ logger
122
+ }, request, shop);
123
+ } else {
124
+ throw await beginAuth.beginAuth({
125
+ api,
126
+ config,
127
+ logger
128
+ }, request, false, shop);
129
+ }
130
+ }
131
+ shop = shop || offlineSession.shop;
132
+ if (config.isEmbeddedApp && !isEmbedded) {
133
+ try {
134
+ logger.debug('Ensuring offline session is valid before embedding', {
135
+ shop
136
+ });
137
+ await this.testSession(offlineSession);
138
+ logger.debug('Offline session is still valid, embedding app', {
139
+ shop
140
+ });
141
+ } catch (error) {
142
+ await this.handleInvalidOfflineSession(error, request, shop);
143
+ }
144
+ }
145
+ }
146
+ async handleAuthBeginRequest(request, shop) {
147
+ const {
148
+ api,
149
+ config,
150
+ logger
151
+ } = this;
152
+ logger.info('Handling OAuth begin request', {
153
+ shop
154
+ });
155
+
156
+ // If we're loading from an iframe, we need to break out of it
157
+ if (config.isEmbeddedApp && request.headers.get('Sec-Fetch-Dest') === 'iframe') {
158
+ logger.debug('Auth request in iframe detected, exiting iframe', {
159
+ shop
160
+ });
161
+ throw redirectWithExitiframe.redirectWithExitIframe({
162
+ api,
163
+ config,
164
+ logger
165
+ }, request, shop);
166
+ } else {
167
+ throw await beginAuth.beginAuth({
168
+ api,
169
+ config,
170
+ logger
171
+ }, request, false, shop);
172
+ }
173
+ }
174
+ async handleAuthCallbackRequest(request, shop) {
175
+ const {
176
+ api,
177
+ config,
178
+ logger
179
+ } = this;
180
+ logger.info('Handling OAuth callback request');
181
+ try {
182
+ const {
183
+ session,
184
+ headers: responseHeaders
185
+ } = await api.auth.callback({
186
+ rawRequest: request
187
+ });
188
+ await config.sessionStorage.storeSession(session);
189
+ if (config.useOnlineTokens && !session.isOnline) {
190
+ logger.info('Requesting online access token for offline session');
191
+ await beginAuth.beginAuth({
192
+ api,
193
+ config,
194
+ logger
195
+ }, request, true, shop);
196
+ }
197
+ await triggerAfterAuthHook.triggerAfterAuthHook({
198
+ api,
199
+ config,
200
+ logger
201
+ }, session, request);
202
+ throw await redirectToShopifyOrAppRoot.redirectToShopifyOrAppRoot(request, {
203
+ api,
204
+ config,
205
+ logger
206
+ }, responseHeaders);
207
+ } catch (error) {
208
+ if (error instanceof Response) throw error;
209
+ throw await this.oauthCallbackError(error, request, shop);
210
+ }
211
+ }
212
+ async getOfflineSession(request) {
213
+ const offlineId = await this.getOfflineSessionId(request);
214
+ return this.config.sessionStorage.loadSession(offlineId);
215
+ }
216
+ async hasValidOfflineId(request) {
217
+ return Boolean(await this.getOfflineSessionId(request));
218
+ }
219
+ async getOfflineSessionId(request) {
220
+ const {
221
+ api
222
+ } = this;
223
+ const url = new URL(request.url);
224
+ const shop = url.searchParams.get('shop');
225
+ return shop ? api.session.getOfflineId(shop) : api.session.getCurrentId({
226
+ isOnline: false,
227
+ rawRequest: request
228
+ });
229
+ }
230
+ async testSession(session) {
231
+ const {
232
+ api
233
+ } = this;
234
+ const client = new api.clients.Graphql({
235
+ session
236
+ });
237
+ await client.query({
238
+ data: `#graphql
239
+ query shopifyAppShopName {
240
+ shop {
241
+ name
242
+ }
243
+ }
244
+ `
245
+ });
246
+ }
247
+ async oauthCallbackError(error, request, shop) {
248
+ const {
249
+ logger
250
+ } = this;
251
+ logger.error('Error during OAuth callback', {
252
+ error: error.message
253
+ });
254
+ if (error instanceof shopifyApi.CookieNotFound) {
255
+ return this.handleAuthBeginRequest(request, shop);
256
+ }
257
+ if (error instanceof shopifyApi.InvalidHmacError || error instanceof shopifyApi.InvalidOAuthError) {
258
+ return new Response(undefined, {
259
+ status: 400,
260
+ statusText: 'Invalid OAuth Request'
261
+ });
262
+ }
263
+ return new Response(undefined, {
264
+ status: 500,
265
+ statusText: 'Internal Server Error'
266
+ });
267
+ }
268
+ async handleInvalidOfflineSession(error, request, shop) {
269
+ const {
270
+ api,
271
+ logger,
272
+ config
273
+ } = this;
274
+ if (error instanceof shopifyApi.HttpResponseError) {
275
+ if (error.response.code === 401) {
276
+ logger.info('Shop session is no longer valid, redirecting to OAuth', {
277
+ shop
278
+ });
279
+ throw await beginAuth.beginAuth({
280
+ api,
281
+ config,
282
+ logger
283
+ }, request, false, shop);
284
+ } else {
285
+ const message = JSON.stringify(error.response.body, null, 2);
286
+ logger.error(`Unexpected error during session validation: ${message}`, {
287
+ shop
288
+ });
289
+ throw new Response(undefined, {
290
+ status: error.response.code,
291
+ statusText: error.response.statusText
292
+ });
293
+ }
294
+ } else if (error instanceof shopifyApi.GraphqlQueryError) {
295
+ const context = {
296
+ shop
297
+ };
298
+ if (error.response) {
299
+ context.response = JSON.stringify(error.response);
300
+ }
301
+ logger.error(`Unexpected error during session validation: ${error.message}`, context);
302
+ throw new Response(undefined, {
303
+ status: 500,
304
+ statusText: 'Internal Server Error'
305
+ });
306
+ }
307
+ }
308
+ }
309
+
310
+ exports.AuthCodeFlowStrategy = AuthCodeFlowStrategy;
@@ -2,9 +2,15 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ const SESSION_TOKEN_PARAM = 'id_token';
5
6
  function getSessionTokenHeader(request) {
6
7
  var _request$headers$get;
7
8
  return (_request$headers$get = request.headers.get('authorization')) === null || _request$headers$get === void 0 ? void 0 : _request$headers$get.replace('Bearer ', '');
8
9
  }
10
+ function getSessionTokenFromUrlParam(request) {
11
+ const url = new URL(request.url);
12
+ return url.searchParams.get(SESSION_TOKEN_PARAM);
13
+ }
9
14
 
15
+ exports.getSessionTokenFromUrlParam = getSessionTokenFromUrlParam;
10
16
  exports.getSessionTokenHeader = getSessionTokenHeader;
@@ -8,7 +8,7 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
8
8
 
9
9
  var isbot__default = /*#__PURE__*/_interopDefaultLegacy(isbot);
10
10
 
11
- function rejectBotRequest({
11
+ function respondToBotRequest({
12
12
  logger
13
13
  }, request) {
14
14
  if (isbot__default["default"](request.headers.get('User-Agent'))) {
@@ -20,4 +20,4 @@ function rejectBotRequest({
20
20
  }
21
21
  }
22
22
 
23
- exports.rejectBotRequest = rejectBotRequest;
23
+ exports.respondToBotRequest = respondToBotRequest;
@@ -15,7 +15,7 @@ function authenticateCheckoutFactory(params) {
15
15
  logger
16
16
  } = params;
17
17
  const corsHeaders = (_options$corsHeaders = options.corsHeaders) !== null && _options$corsHeaders !== void 0 ? _options$corsHeaders : [];
18
- rejectBotRequest.rejectBotRequest(params, request);
18
+ rejectBotRequest.respondToBotRequest(params, request);
19
19
  respondToOptionsRequest.respondToOptionsRequest(params, request, corsHeaders);
20
20
  const sessionTokenHeader = getSessionTokenHeader.getSessionTokenHeader(request);
21
21
  logger.info('Authenticating checkout request');
@@ -2,6 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var shopifyApi = require('@shopify/shopify-api');
5
6
  var factory = require('../../clients/admin/factory.js');
6
7
  require('@shopify/shopify-api/runtime');
7
8
  var handleClientError = require('../admin/helpers/handle-client-error.js');
@@ -30,11 +31,19 @@ function authenticateWebhookFactory(params) {
30
31
  rawRequest: request
31
32
  });
32
33
  if (!check.valid) {
33
- logger.debug('Webhook validation failed', check);
34
- throw new Response(undefined, {
35
- status: 400,
36
- statusText: 'Bad Request'
37
- });
34
+ if (check.reason === shopifyApi.WebhookValidationErrorReason.InvalidHmac) {
35
+ logger.debug('Webhook HMAC validation failed', check);
36
+ throw new Response(undefined, {
37
+ status: 401,
38
+ statusText: 'Unauthorized'
39
+ });
40
+ } else {
41
+ logger.debug('Webhook validation failed', check);
42
+ throw new Response(undefined, {
43
+ status: 400,
44
+ statusText: 'Bad Request'
45
+ });
46
+ }
38
47
  }
39
48
  const sessionId = api.session.getOfflineId(check.domain);
40
49
  const session = await config.sessionStorage.loadSession(sessionId);
@@ -13,22 +13,20 @@ function graphqlClientFactory({
13
13
  handleClientError,
14
14
  session
15
15
  }) {
16
- return async function query(query, options) {
17
- var _options;
16
+ return async function query(operation, options) {
18
17
  const client = new params.api.clients.Graphql({
19
18
  session,
20
- apiVersion: (_options = options) === null || _options === void 0 ? void 0 : _options.apiVersion
19
+ apiVersion: options === null || options === void 0 ? void 0 : options.apiVersion
21
20
  });
22
21
  try {
23
- var _options2, _options3, _options4;
24
22
  // We convert the incoming response to a Response object to bring this client closer to the Remix client.
25
23
  const apiResponse = await client.query({
26
24
  data: {
27
- query,
28
- variables: (_options2 = options) === null || _options2 === void 0 ? void 0 : _options2.variables
25
+ query: operation,
26
+ variables: options === null || options === void 0 ? void 0 : options.variables
29
27
  },
30
- tries: (_options3 = options) === null || _options3 === void 0 ? void 0 : _options3.tries,
31
- extraHeaders: (_options4 = options) === null || _options4 === void 0 ? void 0 : _options4.headers
28
+ tries: options === null || options === void 0 ? void 0 : options.tries,
29
+ extraHeaders: options === null || options === void 0 ? void 0 : options.headers
32
30
  });
33
31
  return new Response(JSON.stringify(apiResponse.body), {
34
32
  headers: runtime.flatHeaders(apiResponse.headers)
@@ -7,8 +7,8 @@ var shopifyApi = require('@shopify/shopify-api');
7
7
  var types = require('./types.js');
8
8
  var version = require('./version.js');
9
9
  var register = require('./authenticate/webhooks/register.js');
10
- var authenticate$1 = require('./authenticate/admin/authenticate.js');
11
- var authenticate = require('./authenticate/webhooks/authenticate.js');
10
+ var authenticate = require('./authenticate/admin/authenticate.js');
11
+ 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');
@@ -16,6 +16,7 @@ var login = require('./authenticate/login/login.js');
16
16
  var factory$1 = require('./unauthenticated/admin/factory.js');
17
17
  var factory = require('./authenticate/public/factory.js');
18
18
  var factory$2 = require('./unauthenticated/storefront/factory.js');
19
+ var authCodeFlow = require('./authenticate/admin/strategies/auth-code-flow.js');
19
20
 
20
21
  /**
21
22
  * Creates an object your app will use to interact with Shopify.
@@ -49,15 +50,19 @@ function shopifyApp(appConfig) {
49
50
  config,
50
51
  logger
51
52
  };
52
- const oauth = new authenticate$1.AuthStrategy(params);
53
+ const oauth = new authCodeFlow.AuthCodeFlowStrategy(params);
54
+ const authStrategy = authenticate.authStrategyFactory({
55
+ ...params,
56
+ strategy: oauth
57
+ });
53
58
  const shopify = {
54
59
  sessionStorage: config.sessionStorage,
55
60
  addDocumentResponseHeaders: addResponseHeaders.addDocumentResponseHeadersFactory(params),
56
61
  registerWebhooks: register.registerWebhooksFactory(params),
57
62
  authenticate: {
58
- admin: oauth.authenticateAdmin.bind(oauth),
63
+ admin: authStrategy,
59
64
  public: factory.authenticatePublicFactory(params),
60
- webhook: authenticate.authenticateWebhookFactory(params)
65
+ webhook: authenticate$1.authenticateWebhookFactory(params)
61
66
  },
62
67
  unauthenticated: {
63
68
  admin: factory$1.unauthenticatedAdminContextFactory(params),
@@ -101,7 +106,8 @@ function deriveApi(appConfig) {
101
106
  userAgentPrefix,
102
107
  isEmbeddedApp: (_appConfig$isEmbedded = appConfig.isEmbeddedApp) !== null && _appConfig$isEmbedded !== void 0 ? _appConfig$isEmbedded : true,
103
108
  apiVersion: (_appConfig$apiVersion = appConfig.apiVersion) !== null && _appConfig$apiVersion !== void 0 ? _appConfig$apiVersion : shopifyApi.LATEST_API_VERSION,
104
- isCustomStoreApp: appConfig.distribution === types.AppDistribution.ShopifyAdmin
109
+ isCustomStoreApp: appConfig.distribution === types.AppDistribution.ShopifyAdmin,
110
+ future: {}
105
111
  });
106
112
  }
107
113
  function deriveConfig(appConfig, apiConfig) {
@@ -2,6 +2,6 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- const SHOPIFY_REMIX_LIBRARY_VERSION = '2.1.0';
5
+ const SHOPIFY_REMIX_LIBRARY_VERSION = '2.2.0';
6
6
 
7
7
  exports.SHOPIFY_REMIX_LIBRARY_VERSION = SHOPIFY_REMIX_LIBRARY_VERSION;