@shopify/shopify-app-remix 2.2.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.
Files changed (154) hide show
  1. package/build/cjs/server/adapters/node/index.js +1 -0
  2. package/build/cjs/server/authenticate/admin/authenticate.js +17 -14
  3. package/build/cjs/server/authenticate/admin/billing/cancel.js +1 -2
  4. package/build/cjs/server/authenticate/admin/billing/request.js +0 -1
  5. package/build/cjs/server/authenticate/admin/billing/require.js +1 -2
  6. package/build/cjs/server/authenticate/admin/helpers/create-admin-api-context.js +2 -5
  7. package/build/cjs/server/authenticate/admin/helpers/handle-client-error.js +8 -4
  8. package/build/cjs/server/authenticate/admin/helpers/redirect-to-bounce-page.js +4 -2
  9. package/build/cjs/server/authenticate/admin/helpers/redirect.js +1 -5
  10. package/build/cjs/server/authenticate/admin/helpers/trigger-after-auth-hook.js +2 -2
  11. package/build/cjs/server/authenticate/admin/strategies/auth-code-flow.js +37 -13
  12. package/build/cjs/server/authenticate/admin/strategies/token-exchange.js +155 -0
  13. package/build/cjs/server/authenticate/const.js +4 -0
  14. package/build/cjs/server/authenticate/helpers/idempotent-promise-handler.js +40 -0
  15. package/build/cjs/server/authenticate/helpers/respond-to-invalid-session-token.js +33 -0
  16. package/build/cjs/server/authenticate/login/login.js +5 -1
  17. package/build/cjs/server/authenticate/public/checkout/authenticate.js +1 -0
  18. package/build/cjs/server/authenticate/webhooks/authenticate.js +1 -2
  19. package/build/cjs/server/authenticate/webhooks/register.js +16 -0
  20. package/build/cjs/server/clients/admin/graphql.js +5 -12
  21. package/build/cjs/server/clients/storefront/factory.js +5 -12
  22. package/build/cjs/server/shopify-app.js +12 -6
  23. package/build/cjs/server/version.js +1 -1
  24. package/build/ts/react/components/AppProvider/AppProvider.js +1 -1
  25. package/build/ts/react/components/AppProvider/AppProvider.js.map +1 -1
  26. package/build/ts/server/__test-helpers/const.d.ts +2 -1
  27. package/build/ts/server/__test-helpers/const.d.ts.map +1 -1
  28. package/build/ts/server/__test-helpers/const.js +3 -2
  29. package/build/ts/server/__test-helpers/const.js.map +1 -1
  30. package/build/ts/server/__test-helpers/expect-admin-api-client.d.ts.map +1 -1
  31. package/build/ts/server/__test-helpers/expect-admin-api-client.js +2 -2
  32. package/build/ts/server/__test-helpers/expect-admin-api-client.js.map +1 -1
  33. package/build/ts/server/__test-helpers/expect-document-request-headers.js.map +1 -1
  34. package/build/ts/server/__test-helpers/expect-exit-iframe.js.map +1 -1
  35. package/build/ts/server/__test-helpers/expect-storefront-api-client.js +1 -1
  36. package/build/ts/server/__test-helpers/expect-storefront-api-client.js.map +1 -1
  37. package/build/ts/server/__test-helpers/get-thrown-response.js.map +1 -1
  38. package/build/ts/server/__test-helpers/request-mock.d.ts.map +1 -1
  39. package/build/ts/server/__test-helpers/request-mock.js +1 -1
  40. package/build/ts/server/__test-helpers/request-mock.js.map +1 -1
  41. package/build/ts/server/__test-helpers/setup-valid-session.d.ts +1 -1
  42. package/build/ts/server/__test-helpers/setup-valid-session.d.ts.map +1 -1
  43. package/build/ts/server/__test-helpers/setup-valid-session.js +5 -5
  44. package/build/ts/server/__test-helpers/setup-valid-session.js.map +1 -1
  45. package/build/ts/server/__test-helpers/test-config.d.ts +2 -1
  46. package/build/ts/server/__test-helpers/test-config.d.ts.map +1 -1
  47. package/build/ts/server/__test-helpers/test-config.js +1 -0
  48. package/build/ts/server/__test-helpers/test-config.js.map +1 -1
  49. package/build/ts/server/adapters/node/index.js.map +1 -1
  50. package/build/ts/server/authenticate/admin/authenticate.d.ts +7 -1
  51. package/build/ts/server/authenticate/admin/authenticate.d.ts.map +1 -1
  52. package/build/ts/server/authenticate/admin/authenticate.js +14 -14
  53. package/build/ts/server/authenticate/admin/authenticate.js.map +1 -1
  54. package/build/ts/server/authenticate/admin/billing/request.js.map +1 -1
  55. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts +2 -2
  56. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts.map +1 -1
  57. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js +2 -5
  58. package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js.map +1 -1
  59. package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js.map +1 -1
  60. package/build/ts/server/authenticate/admin/helpers/handle-client-error.d.ts +2 -5
  61. package/build/ts/server/authenticate/admin/helpers/handle-client-error.d.ts.map +1 -1
  62. package/build/ts/server/authenticate/admin/helpers/handle-client-error.js +6 -5
  63. package/build/ts/server/authenticate/admin/helpers/handle-client-error.js.map +1 -1
  64. package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.d.ts.map +1 -1
  65. package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js +4 -2
  66. package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js.map +1 -1
  67. package/build/ts/server/authenticate/admin/helpers/redirect-with-exitiframe.js.map +1 -1
  68. package/build/ts/server/authenticate/admin/helpers/redirect.js +0 -3
  69. package/build/ts/server/authenticate/admin/helpers/redirect.js.map +1 -1
  70. package/build/ts/server/authenticate/admin/helpers/render-app-bridge.js.map +1 -1
  71. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts +2 -1
  72. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts.map +1 -1
  73. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js +2 -2
  74. package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js.map +1 -1
  75. package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.js.map +1 -1
  76. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts +4 -2
  77. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -1
  78. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js +27 -7
  79. package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -1
  80. package/build/ts/server/authenticate/admin/strategies/token-exchange.d.ts +17 -0
  81. package/build/ts/server/authenticate/admin/strategies/token-exchange.d.ts.map +1 -0
  82. package/build/ts/server/authenticate/admin/strategies/token-exchange.js +163 -0
  83. package/build/ts/server/authenticate/admin/strategies/token-exchange.js.map +1 -0
  84. package/build/ts/server/authenticate/admin/strategies/types.d.ts +16 -5
  85. package/build/ts/server/authenticate/admin/strategies/types.d.ts.map +1 -1
  86. package/build/ts/server/authenticate/admin/types.d.ts +0 -4
  87. package/build/ts/server/authenticate/admin/types.d.ts.map +1 -1
  88. package/build/ts/server/authenticate/const.d.ts +3 -0
  89. package/build/ts/server/authenticate/const.d.ts.map +1 -1
  90. package/build/ts/server/authenticate/const.js +4 -1
  91. package/build/ts/server/authenticate/const.js.map +1 -1
  92. package/build/ts/server/authenticate/helpers/add-response-headers.js.map +1 -1
  93. package/build/ts/server/authenticate/helpers/ensure-cors-headers.js.map +1 -1
  94. package/build/ts/server/authenticate/helpers/idempotent-promise-handler.d.ts +12 -0
  95. package/build/ts/server/authenticate/helpers/idempotent-promise-handler.d.ts.map +1 -0
  96. package/build/ts/server/authenticate/helpers/idempotent-promise-handler.js +53 -0
  97. package/build/ts/server/authenticate/helpers/idempotent-promise-handler.js.map +1 -0
  98. package/build/ts/server/authenticate/helpers/index.d.ts +1 -0
  99. package/build/ts/server/authenticate/helpers/index.d.ts.map +1 -1
  100. package/build/ts/server/authenticate/helpers/index.js +1 -0
  101. package/build/ts/server/authenticate/helpers/index.js.map +1 -1
  102. package/build/ts/server/authenticate/helpers/reject-bot-request.js.map +1 -1
  103. package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.d.ts +9 -0
  104. package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.d.ts.map +1 -0
  105. package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.js +20 -0
  106. package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.js.map +1 -0
  107. package/build/ts/server/authenticate/helpers/respond-to-options-request.js.map +1 -1
  108. package/build/ts/server/authenticate/login/login.d.ts.map +1 -1
  109. package/build/ts/server/authenticate/login/login.js +6 -2
  110. package/build/ts/server/authenticate/login/login.js.map +1 -1
  111. package/build/ts/server/authenticate/public/appProxy/authenticate.js.map +1 -1
  112. package/build/ts/server/authenticate/public/checkout/authenticate.js.map +1 -1
  113. package/build/ts/server/authenticate/public/factory.js.map +1 -1
  114. package/build/ts/server/authenticate/webhooks/authenticate.js.map +1 -1
  115. package/build/ts/server/authenticate/webhooks/register.d.ts +1 -1
  116. package/build/ts/server/authenticate/webhooks/register.d.ts.map +1 -1
  117. package/build/ts/server/authenticate/webhooks/register.js +20 -1
  118. package/build/ts/server/authenticate/webhooks/register.js.map +1 -1
  119. package/build/ts/server/authenticate/webhooks/types.d.ts +12 -5
  120. package/build/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
  121. package/build/ts/server/boundary/error.js.map +1 -1
  122. package/build/ts/server/boundary/headers.js.map +1 -1
  123. package/build/ts/server/clients/admin/graphql.d.ts +3 -3
  124. package/build/ts/server/clients/admin/graphql.d.ts.map +1 -1
  125. package/build/ts/server/clients/admin/graphql.js +5 -8
  126. package/build/ts/server/clients/admin/graphql.js.map +1 -1
  127. package/build/ts/server/clients/admin/rest.js.map +1 -1
  128. package/build/ts/server/clients/admin/types.d.ts +2 -1
  129. package/build/ts/server/clients/admin/types.d.ts.map +1 -1
  130. package/build/ts/server/clients/storefront/factory.d.ts.map +1 -1
  131. package/build/ts/server/clients/storefront/factory.js +5 -8
  132. package/build/ts/server/clients/storefront/factory.js.map +1 -1
  133. package/build/ts/server/clients/storefront/types.d.ts +2 -1
  134. package/build/ts/server/clients/storefront/types.d.ts.map +1 -1
  135. package/build/ts/server/clients/types.d.ts +4 -7
  136. package/build/ts/server/clients/types.d.ts.map +1 -1
  137. package/build/ts/server/config-types.d.ts +2 -0
  138. package/build/ts/server/config-types.d.ts.map +1 -1
  139. package/build/ts/server/future/flags.d.ts +6 -0
  140. package/build/ts/server/future/flags.d.ts.map +1 -1
  141. package/build/ts/server/override-logger.js.map +1 -1
  142. package/build/ts/server/shopify-app.d.ts +1 -0
  143. package/build/ts/server/shopify-app.d.ts.map +1 -1
  144. package/build/ts/server/shopify-app.js +11 -5
  145. package/build/ts/server/shopify-app.js.map +1 -1
  146. package/build/ts/server/types.d.ts +10 -4
  147. package/build/ts/server/types.d.ts.map +1 -1
  148. package/build/ts/server/types.js +2 -2
  149. package/build/ts/server/types.js.map +1 -1
  150. package/build/ts/server/unauthenticated/admin/factory.js.map +1 -1
  151. package/build/ts/server/unauthenticated/storefront/factory.js.map +1 -1
  152. package/build/ts/server/version.d.ts +1 -1
  153. package/build/ts/server/version.js +1 -1
  154. package/package.json +14 -16
@@ -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, session, {
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, existingSession, shop);
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,9 +3,8 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var shopifyApi = require('@shopify/shopify-api');
6
- require('@shopify/shopify-api/runtime');
7
- var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
8
6
  require('@remix-run/server-runtime');
7
+ var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
9
8
 
10
9
  function cancelBillingFactory(params, request, session) {
11
10
  return async function cancelBilling(options) {
@@ -4,7 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var shopifyApi = require('@shopify/shopify-api');
6
6
  var serverRuntime = require('@remix-run/server-runtime');
7
- require('@shopify/shopify-api/runtime');
8
7
  var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
9
8
  var redirectWithAppBridgeHeaders = require('../helpers/redirect-with-app-bridge-headers.js');
10
9
 
@@ -3,9 +3,8 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var shopifyApi = require('@shopify/shopify-api');
6
- require('@shopify/shopify-api/runtime');
7
- var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
8
6
  require('@remix-run/server-runtime');
7
+ var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
9
8
 
10
9
  function requireBillingFactory(params, request, session) {
11
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(request, session, params) {
7
+ function createAdminApiContext(session, params, handleClientError) {
9
8
  return factory.adminClientFactory({
10
9
  session,
11
10
  params,
12
- handleClientError: handleClientError.handleClientErrorFactory({
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 (error.response.code === 401) {
26
- throw await redirectToAuthPage.redirectToAuthPage(params, request, session.shop);
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
- url.searchParams.set('shopify-reload', `${config.appUrl}${url.pathname}${url.search}`);
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}${url.search}`);
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
- const sessionTokenSearchParam = searchParams.has('id_token');
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(request, session, params)
16
+ admin: createAdminApiContext.createAdminApiContext(session, params, authStrategy.handleClientError(request))
17
17
  });
18
18
  }
19
19
  }
@@ -4,10 +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
- require('@shopify/shopify-api/runtime');
8
- var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
9
7
  var redirectToShopifyOrAppRoot = require('../helpers/redirect-to-shopify-or-app-root.js');
10
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');
11
11
  var redirectWithExitiframe = require('../helpers/redirect-with-exitiframe.js');
12
12
  var getSessionTokenHeader = require('../../helpers/get-session-token-header.js');
13
13
  var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
@@ -53,12 +53,16 @@ class AuthCodeFlowStrategy {
53
53
  await this.ensureInstalledOnShop(request);
54
54
  }
55
55
  }
56
- async authenticate(request, session, shop) {
56
+ async authenticate(request, sessionContext) {
57
57
  const {
58
58
  api,
59
59
  config,
60
60
  logger
61
61
  } = this;
62
+ const {
63
+ shop,
64
+ session
65
+ } = sessionContext;
62
66
  if (!session) {
63
67
  logger.debug('No session found, redirecting to OAuth', {
64
68
  shop
@@ -83,6 +87,28 @@ class AuthCodeFlowStrategy {
83
87
  });
84
88
  return session;
85
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
+ }
86
112
  async ensureInstalledOnShop(request) {
87
113
  const {
88
114
  api,
@@ -198,7 +224,7 @@ class AuthCodeFlowStrategy {
198
224
  api,
199
225
  config,
200
226
  logger
201
- }, session, request);
227
+ }, session, request, this);
202
228
  throw await redirectToShopifyOrAppRoot.redirectToShopifyOrAppRoot(request, {
203
229
  api,
204
230
  config,
@@ -234,15 +260,13 @@ class AuthCodeFlowStrategy {
234
260
  const client = new api.clients.Graphql({
235
261
  session
236
262
  });
237
- await client.query({
238
- data: `#graphql
239
- query shopifyAppShopName {
240
- shop {
241
- name
242
- }
263
+ await client.request(`#graphql
264
+ query shopifyAppShopName {
265
+ shop {
266
+ name
243
267
  }
244
- `
245
- });
268
+ }
269
+ `);
246
270
  }
247
271
  async oauthCallbackError(error, request, shop) {
248
272
  const {
@@ -296,7 +320,7 @@ class AuthCodeFlowStrategy {
296
320
  shop
297
321
  };
298
322
  if (error.response) {
299
- context.response = JSON.stringify(error.response);
323
+ context.response = JSON.stringify(error.body);
300
324
  }
301
325
  logger.error(`Unexpected error during session validation: ${error.message}`, context);
302
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 redirectUrl = `${config.appUrl}${config.auth.path}?shop=${sanitizedShop}`;
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,9 +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
- require('@shopify/shopify-api/runtime');
8
- var handleClientError = require('../admin/helpers/handle-client-error.js');
9
7
  require('@remix-run/server-runtime');
8
+ var handleClientError = require('../admin/helpers/handle-client-error.js');
10
9
 
11
10
  function authenticateWebhookFactory(params) {
12
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
  }
@@ -2,8 +2,6 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var runtime = require('@shopify/shopify-api/runtime');
6
-
7
5
  // eslint-disable-next-line no-warning-comments
8
6
  // TODO: This is actually just a call through to the Shopify API client, but with a different API. We should eventually
9
7
  // move this over to the library layer. While doing that, we should also allow the apiVersion to be passed into the REST
@@ -20,17 +18,12 @@ function graphqlClientFactory({
20
18
  });
21
19
  try {
22
20
  // We convert the incoming response to a Response object to bring this client closer to the Remix client.
23
- const apiResponse = await client.query({
24
- data: {
25
- query: operation,
26
- variables: options === null || options === void 0 ? void 0 : options.variables
27
- },
28
- tries: options === null || options === void 0 ? void 0 : options.tries,
29
- extraHeaders: options === null || options === void 0 ? void 0 : options.headers
30
- });
31
- return new Response(JSON.stringify(apiResponse.body), {
32
- headers: runtime.flatHeaders(apiResponse.headers)
21
+ const apiResponse = await client.request(operation, {
22
+ variables: options === null || options === void 0 ? void 0 : options.variables,
23
+ retries: options !== null && options !== void 0 && options.tries ? options.tries - 1 : 0,
24
+ headers: options === null || options === void 0 ? void 0 : options.headers
33
25
  });
26
+ return new Response(JSON.stringify(apiResponse));
34
27
  } catch (error) {
35
28
  if (handleClientError) {
36
29
  throw await handleClientError({