@shopify/shopify-app-remix 1.0.0-rc.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 (161) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +373 -0
  3. package/build/cjs/adapters/node/index.js +13 -0
  4. package/build/cjs/auth/admin/authenticate.js +599 -0
  5. package/build/cjs/auth/admin/graphql-client.js +57 -0
  6. package/build/cjs/auth/admin/rest-client.js +124 -0
  7. package/build/cjs/auth/const.js +17 -0
  8. package/build/cjs/auth/helpers/add-response-headers.js +69 -0
  9. package/build/cjs/auth/helpers/begin-auth.js +18 -0
  10. package/build/cjs/auth/helpers/get-session-token-header.js +10 -0
  11. package/build/cjs/auth/helpers/handle-client-error.js +37 -0
  12. package/build/cjs/auth/helpers/redirect-to-auth-page.js +22 -0
  13. package/build/cjs/auth/helpers/redirect-with-app-bridge-headers.js +31 -0
  14. package/build/cjs/auth/helpers/redirect-with-exitiframe.js +23 -0
  15. package/build/cjs/auth/helpers/reject-bot-request.js +23 -0
  16. package/build/cjs/auth/helpers/validate-session-token.js +25 -0
  17. package/build/cjs/auth/login/login.js +54 -0
  18. package/build/cjs/auth/public/authenticate.js +32 -0
  19. package/build/cjs/auth/webhooks/authenticate.js +68 -0
  20. package/build/cjs/auth/webhooks/register.js +40 -0
  21. package/build/cjs/billing/cancel.js +40 -0
  22. package/build/cjs/billing/request.js +83 -0
  23. package/build/cjs/billing/require.js +47 -0
  24. package/build/cjs/index.js +140 -0
  25. package/build/cjs/override-logger.js +55 -0
  26. package/build/cjs/types.js +25 -0
  27. package/build/cjs/version.js +7 -0
  28. package/build/ts/adapters/node/index.d.ts +2 -0
  29. package/build/ts/adapters/node/index.d.ts.map +1 -0
  30. package/build/ts/adapters/node/index.js +10 -0
  31. package/build/ts/adapters/node/index.js.map +1 -0
  32. package/build/ts/auth/admin/authenticate.d.ts +32 -0
  33. package/build/ts/auth/admin/authenticate.d.ts.map +1 -0
  34. package/build/ts/auth/admin/authenticate.js +571 -0
  35. package/build/ts/auth/admin/authenticate.js.map +1 -0
  36. package/build/ts/auth/admin/graphql-client.d.ts +22 -0
  37. package/build/ts/auth/admin/graphql-client.d.ts.map +1 -0
  38. package/build/ts/auth/admin/graphql-client.js +53 -0
  39. package/build/ts/auth/admin/graphql-client.js.map +1 -0
  40. package/build/ts/auth/admin/rest-client.d.ts +34 -0
  41. package/build/ts/auth/admin/rest-client.d.ts.map +1 -0
  42. package/build/ts/auth/admin/rest-client.js +134 -0
  43. package/build/ts/auth/admin/rest-client.js.map +1 -0
  44. package/build/ts/auth/admin/types.d.ts +100 -0
  45. package/build/ts/auth/admin/types.d.ts.map +1 -0
  46. package/build/ts/auth/admin/types.js +3 -0
  47. package/build/ts/auth/admin/types.js.map +1 -0
  48. package/build/ts/auth/const.d.ts +9 -0
  49. package/build/ts/auth/const.d.ts.map +1 -0
  50. package/build/ts/auth/const.js +12 -0
  51. package/build/ts/auth/const.js.map +1 -0
  52. package/build/ts/auth/helpers/add-response-headers.d.ts +5 -0
  53. package/build/ts/auth/helpers/add-response-headers.d.ts.map +1 -0
  54. package/build/ts/auth/helpers/add-response-headers.js +60 -0
  55. package/build/ts/auth/helpers/add-response-headers.js.map +1 -0
  56. package/build/ts/auth/helpers/begin-auth.d.ts +3 -0
  57. package/build/ts/auth/helpers/begin-auth.d.ts.map +1 -0
  58. package/build/ts/auth/helpers/begin-auth.js +24 -0
  59. package/build/ts/auth/helpers/begin-auth.js.map +1 -0
  60. package/build/ts/auth/helpers/get-session-token-header.d.ts +2 -0
  61. package/build/ts/auth/helpers/get-session-token-header.d.ts.map +1 -0
  62. package/build/ts/auth/helpers/get-session-token-header.js +9 -0
  63. package/build/ts/auth/helpers/get-session-token-header.js.map +1 -0
  64. package/build/ts/auth/helpers/handle-client-error.d.ts +11 -0
  65. package/build/ts/auth/helpers/handle-client-error.d.ts.map +1 -0
  66. package/build/ts/auth/helpers/handle-client-error.js +40 -0
  67. package/build/ts/auth/helpers/handle-client-error.js.map +1 -0
  68. package/build/ts/auth/helpers/index.d.ts +8 -0
  69. package/build/ts/auth/helpers/index.d.ts.map +1 -0
  70. package/build/ts/auth/helpers/index.js +18 -0
  71. package/build/ts/auth/helpers/index.js.map +1 -0
  72. package/build/ts/auth/helpers/redirect-to-auth-page.d.ts +3 -0
  73. package/build/ts/auth/helpers/redirect-to-auth-page.d.ts.map +1 -0
  74. package/build/ts/auth/helpers/redirect-to-auth-page.js +33 -0
  75. package/build/ts/auth/helpers/redirect-to-auth-page.js.map +1 -0
  76. package/build/ts/auth/helpers/redirect-with-app-bridge-headers.d.ts +4 -0
  77. package/build/ts/auth/helpers/redirect-with-app-bridge-headers.d.ts.map +1 -0
  78. package/build/ts/auth/helpers/redirect-with-app-bridge-headers.js +24 -0
  79. package/build/ts/auth/helpers/redirect-with-app-bridge-headers.js.map +1 -0
  80. package/build/ts/auth/helpers/redirect-with-exitiframe.d.ts +3 -0
  81. package/build/ts/auth/helpers/redirect-with-exitiframe.d.ts.map +1 -0
  82. package/build/ts/auth/helpers/redirect-with-exitiframe.js +18 -0
  83. package/build/ts/auth/helpers/redirect-with-exitiframe.js.map +1 -0
  84. package/build/ts/auth/helpers/reject-bot-request.d.ts +3 -0
  85. package/build/ts/auth/helpers/reject-bot-request.d.ts.map +1 -0
  86. package/build/ts/auth/helpers/reject-bot-request.js +14 -0
  87. package/build/ts/auth/helpers/reject-bot-request.js.map +1 -0
  88. package/build/ts/auth/helpers/validate-session-token.d.ts +4 -0
  89. package/build/ts/auth/helpers/validate-session-token.d.ts.map +1 -0
  90. package/build/ts/auth/helpers/validate-session-token.js +36 -0
  91. package/build/ts/auth/helpers/validate-session-token.js.map +1 -0
  92. package/build/ts/auth/login/login.d.ts +3 -0
  93. package/build/ts/auth/login/login.d.ts.map +1 -0
  94. package/build/ts/auth/login/login.js +56 -0
  95. package/build/ts/auth/login/login.js.map +1 -0
  96. package/build/ts/auth/public/authenticate.d.ts +4 -0
  97. package/build/ts/auth/public/authenticate.d.ts.map +1 -0
  98. package/build/ts/auth/public/authenticate.js +35 -0
  99. package/build/ts/auth/public/authenticate.js.map +1 -0
  100. package/build/ts/auth/public/types.d.ts +27 -0
  101. package/build/ts/auth/public/types.d.ts.map +1 -0
  102. package/build/ts/auth/public/types.js +3 -0
  103. package/build/ts/auth/public/types.js.map +1 -0
  104. package/build/ts/auth/webhooks/authenticate.d.ts +5 -0
  105. package/build/ts/auth/webhooks/authenticate.d.ts.map +1 -0
  106. package/build/ts/auth/webhooks/authenticate.js +71 -0
  107. package/build/ts/auth/webhooks/authenticate.js.map +1 -0
  108. package/build/ts/auth/webhooks/index.d.ts +2 -0
  109. package/build/ts/auth/webhooks/index.d.ts.map +1 -0
  110. package/build/ts/auth/webhooks/index.js +6 -0
  111. package/build/ts/auth/webhooks/index.js.map +1 -0
  112. package/build/ts/auth/webhooks/register.d.ts +4 -0
  113. package/build/ts/auth/webhooks/register.d.ts.map +1 -0
  114. package/build/ts/auth/webhooks/register.js +39 -0
  115. package/build/ts/auth/webhooks/register.js.map +1 -0
  116. package/build/ts/auth/webhooks/types.d.ts +18 -0
  117. package/build/ts/auth/webhooks/types.d.ts.map +1 -0
  118. package/build/ts/auth/webhooks/types.js +3 -0
  119. package/build/ts/auth/webhooks/types.js.map +1 -0
  120. package/build/ts/billing/cancel.d.ts +5 -0
  121. package/build/ts/billing/cancel.d.ts.map +1 -0
  122. package/build/ts/billing/cancel.js +43 -0
  123. package/build/ts/billing/cancel.js.map +1 -0
  124. package/build/ts/billing/index.d.ts +4 -0
  125. package/build/ts/billing/index.d.ts.map +1 -0
  126. package/build/ts/billing/index.js +10 -0
  127. package/build/ts/billing/index.js.map +1 -0
  128. package/build/ts/billing/request.d.ts +6 -0
  129. package/build/ts/billing/request.d.ts.map +1 -0
  130. package/build/ts/billing/request.js +84 -0
  131. package/build/ts/billing/request.js.map +1 -0
  132. package/build/ts/billing/require.d.ts +6 -0
  133. package/build/ts/billing/require.d.ts.map +1 -0
  134. package/build/ts/billing/require.js +55 -0
  135. package/build/ts/billing/require.js.map +1 -0
  136. package/build/ts/billing/types.d.ts +228 -0
  137. package/build/ts/billing/types.d.ts.map +1 -0
  138. package/build/ts/billing/types.js +3 -0
  139. package/build/ts/billing/types.js.map +1 -0
  140. package/build/ts/config-types.d.ts +325 -0
  141. package/build/ts/config-types.d.ts.map +1 -0
  142. package/build/ts/config-types.js +3 -0
  143. package/build/ts/config-types.js.map +1 -0
  144. package/build/ts/index.d.ts +29 -0
  145. package/build/ts/index.d.ts.map +1 -0
  146. package/build/ts/index.js +108 -0
  147. package/build/ts/index.js.map +1 -0
  148. package/build/ts/override-logger.d.ts +3 -0
  149. package/build/ts/override-logger.d.ts.map +1 -0
  150. package/build/ts/override-logger.js +40 -0
  151. package/build/ts/override-logger.js.map +1 -0
  152. package/build/ts/types.d.ts +353 -0
  153. package/build/ts/types.d.ts.map +1 -0
  154. package/build/ts/types.js +17 -0
  155. package/build/ts/types.js.map +1 -0
  156. package/build/ts/version.d.ts +2 -0
  157. package/build/ts/version.d.ts.map +1 -0
  158. package/build/ts/version.js +5 -0
  159. package/build/ts/version.js.map +1 -0
  160. package/build/tsconfig.tsbuildinfo +1 -0
  161. package/package.json +65 -0
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ require('@remix-run/server-runtime');
6
+ require('isbot');
7
+ var handleClientError = require('../helpers/handle-client-error.js');
8
+
9
+ class RemixRestClient {
10
+ constructor({
11
+ params,
12
+ request,
13
+ session
14
+ }) {
15
+ this.session = void 0;
16
+ this.resources = void 0;
17
+ this.params = void 0;
18
+ this.request = void 0;
19
+ this.params = params;
20
+ this.request = request;
21
+ this.session = session;
22
+ }
23
+
24
+ /**
25
+ * Performs a GET request on the given path.
26
+ */
27
+ async get(params) {
28
+ return this.makeRequest({
29
+ method: 'GET',
30
+ ...params
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Performs a POST request on the given path.
36
+ */
37
+ async post(params) {
38
+ return this.makeRequest({
39
+ method: 'POST',
40
+ ...params
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Performs a PUT request on the given path.
46
+ */
47
+ async put(params) {
48
+ return this.makeRequest({
49
+ method: 'PUT',
50
+ ...params
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Performs a DELETE request on the given path.
56
+ */
57
+ async delete(params) {
58
+ return this.makeRequest({
59
+ method: 'DELETE',
60
+ ...params
61
+ });
62
+ }
63
+ async makeRequest(params) {
64
+ const originalClient = new this.params.api.clients.Rest({
65
+ session: this.session
66
+ });
67
+ const originalRequest = Reflect.get(originalClient, 'request');
68
+ try {
69
+ const apiResponse = await originalRequest.call(originalClient, params);
70
+
71
+ // We use a separate client for REST requests and REST resources because we want to override the API library
72
+ // client class to return a Response object instead.
73
+ return new Response(JSON.stringify(apiResponse.body), {
74
+ headers: apiResponse.headers
75
+ });
76
+ } catch (error) {
77
+ throw await handleClientError.handleClientError({
78
+ params: this.params,
79
+ request: this.request,
80
+ shop: this.session.shop,
81
+ error
82
+ });
83
+ }
84
+ }
85
+ }
86
+ function restResourceClientFactory({
87
+ params,
88
+ request,
89
+ session
90
+ }) {
91
+ const {
92
+ api,
93
+ config,
94
+ logger
95
+ } = params;
96
+ const ApiClient = api.clients.Rest;
97
+ return class RestResourceClient extends ApiClient {
98
+ async request(params) {
99
+ const originalClient = new api.clients.Rest({
100
+ session
101
+ });
102
+ const originalRequest = Reflect.get(originalClient, 'request');
103
+ try {
104
+ // We just call through to the API library client, and handle the error response here, so that data parsing
105
+ // behaves the same way.
106
+ return await originalRequest.call(originalClient, params);
107
+ } catch (error) {
108
+ throw await handleClientError.handleClientError({
109
+ params: {
110
+ api,
111
+ config,
112
+ logger
113
+ },
114
+ request,
115
+ shop: session.shop,
116
+ error
117
+ });
118
+ }
119
+ }
120
+ };
121
+ }
122
+
123
+ exports.RemixRestClient = RemixRestClient;
124
+ exports.restResourceClientFactory = restResourceClientFactory;
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const APP_BRIDGE_NEXT_URL = 'https://cdn.shopify.com/shopifycloud/app-bridge-next.js';
6
+ const REAUTH_URL_HEADER = 'X-Shopify-API-Request-Failure-Reauthorize-Url';
7
+ const APP_BRIDGE_HEADERS = {
8
+ 'Access-Control-Allow-Origin': '*',
9
+ 'Access-Control-Allow-Headers': 'Authorization',
10
+ 'Access-Control-Expose-Headers': REAUTH_URL_HEADER
11
+ };
12
+ const DEFAULT_CSP_VALUE = `frame-ancestors https://*.myshopify.com https://admin.shopify.com;`;
13
+
14
+ exports.APP_BRIDGE_HEADERS = APP_BRIDGE_HEADERS;
15
+ exports.APP_BRIDGE_NEXT_URL = APP_BRIDGE_NEXT_URL;
16
+ exports.DEFAULT_CSP_VALUE = DEFAULT_CSP_VALUE;
17
+ exports.REAUTH_URL_HEADER = REAUTH_URL_HEADER;
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _const = require('../const.js');
6
+
7
+ const ORIGINAL_HEADERS = Symbol.for('originalHeaders');
8
+ function addResponseHeadersFactory(params) {
9
+ const {
10
+ api,
11
+ config
12
+ } = params;
13
+ return function (request, headers) {
14
+ const {
15
+ searchParams
16
+ } = new URL(request.url);
17
+ const shop = api.utils.sanitizeShop(searchParams.get('shop'));
18
+ addResponseHeaders(headers, config.isEmbeddedApp, shop);
19
+ };
20
+ }
21
+ function addResponseHeaders(headers, isEmbeddedApp, shop) {
22
+ if (isEmbeddedApp && shop) {
23
+ // Set or update the CSP with the shop subdomain instead of a wildcard:
24
+ let csp = headers.get('Content-Security-Policy') || _const.DEFAULT_CSP_VALUE;
25
+ if (shop) csp = csp.replace('*.myshopify.com', shop);
26
+ headers.set('Content-Security-Policy', csp);
27
+ }
28
+ }
29
+ function installGlobalResponseHeaders(isEmbeddedApp) {
30
+ const headersDescriptor = Object.getOwnPropertyDescriptor(Response.prototype, ORIGINAL_HEADERS) || Object.getOwnPropertyDescriptor(Response.prototype, 'headers');
31
+
32
+ // Store the original Response.prototype.headers descriptor so we can use it when live-reloading the server:
33
+ Object.defineProperty(Response.prototype, ORIGINAL_HEADERS, headersDescriptor);
34
+
35
+ // Overwrite the Response.prototype.headers getter with one that injects our global headers:
36
+ Object.defineProperty(Response.prototype, 'headers', {
37
+ ...headersDescriptor,
38
+ get() {
39
+ // Note: this is not an empty Headers, it will contain any headers passed to new Response():
40
+ const headers = headersDescriptor.get.call(this);
41
+ try {
42
+ // Inject CORS headers required by both Embedded Apps and UI Extensions:
43
+ if (isEmbeddedApp) {
44
+ for (const key in _const.APP_BRIDGE_HEADERS) {
45
+ if (!headers.get(key)) {
46
+ const value = _const.APP_BRIDGE_HEADERS[key];
47
+ headers.set(key, value);
48
+ }
49
+ }
50
+ }
51
+
52
+ // Apply a default CSP unless a more specific one (no wildcard) is present:
53
+ if (!headers.get('Content-Security-Policy')) {
54
+ headers.set('Content-Security-Policy', isEmbeddedApp ? _const.DEFAULT_CSP_VALUE : `frame-ancestors 'none';`);
55
+ }
56
+ if (!headers.get('Link')) {
57
+ headers.set('Link', `<${_const.APP_BRIDGE_NEXT_URL}>; rel="preload"`);
58
+ }
59
+ } catch (err) {
60
+ // Do nothing, this is not a standard Response object.
61
+ }
62
+ return headers;
63
+ }
64
+ });
65
+ }
66
+
67
+ exports.addResponseHeaders = addResponseHeaders;
68
+ exports.addResponseHeadersFactory = addResponseHeadersFactory;
69
+ exports.installGlobalResponseHeaders = installGlobalResponseHeaders;
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ async function beginAuth(params, request, isOnline, shop) {
6
+ const {
7
+ api,
8
+ config
9
+ } = params;
10
+ throw await api.auth.begin({
11
+ shop,
12
+ callbackPath: config.auth.callbackPath,
13
+ isOnline,
14
+ rawRequest: request
15
+ });
16
+ }
17
+
18
+ exports.beginAuth = beginAuth;
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ function getSessionTokenHeader(request) {
6
+ var _request$headers$get;
7
+ return (_request$headers$get = request.headers.get('authorization')) === null || _request$headers$get === void 0 ? void 0 : _request$headers$get.replace('Bearer ', '');
8
+ }
9
+
10
+ exports.getSessionTokenHeader = getSessionTokenHeader;
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var shopifyApi = require('@shopify/shopify-api');
6
+ var redirectToAuthPage = require('./redirect-to-auth-page.js');
7
+
8
+ async function handleClientError({
9
+ params,
10
+ request,
11
+ error,
12
+ shop
13
+ }) {
14
+ if (error instanceof shopifyApi.HttpResponseError) {
15
+ params.logger.debug(`Got an HTTP response error from the API: ${error.message}`, {
16
+ code: error.response.code,
17
+ statusText: error.response.statusText,
18
+ body: JSON.stringify(error.response.body)
19
+ });
20
+ if (error.response.code === 401) {
21
+ throw await redirectToAuthPage.redirectToAuthPage(params, request, shop);
22
+ } else {
23
+ // forward a minimal copy of the upstream HTTP response instead of an Error:
24
+ throw new Response(JSON.stringify(error.response.body), {
25
+ status: error.response.code,
26
+ headers: {
27
+ 'Content-Type': error.response.headers['Content-Type']
28
+ }
29
+ });
30
+ }
31
+ } else {
32
+ params.logger.debug(`Got a response error from the API: ${error.message}`);
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ exports.handleClientError = handleClientError;
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var beginAuth = require('./begin-auth.js');
6
+ var redirectWithExitiframe = require('./redirect-with-exitiframe.js');
7
+ var redirectWithAppBridgeHeaders = require('./redirect-with-app-bridge-headers.js');
8
+
9
+ async function redirectToAuthPage(params, request, shop, isOnline = false) {
10
+ const url = new URL(request.url);
11
+ const isEmbeddedRequest = url.searchParams.get('embedded') === '1';
12
+ const isXhrRequest = request.headers.get('authorization');
13
+ if (isXhrRequest) {
14
+ redirectWithAppBridgeHeaders.redirectWithAppBridgeHeaders(params, shop);
15
+ } else if (isEmbeddedRequest) {
16
+ redirectWithExitiframe.redirectWithExitIframe(params, request, shop);
17
+ } else {
18
+ throw await beginAuth.beginAuth(params, request, isOnline, shop);
19
+ }
20
+ }
21
+
22
+ exports.redirectToAuthPage = redirectToAuthPage;
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _const = require('../const.js');
6
+ var addResponseHeaders = require('./add-response-headers.js');
7
+
8
+ function redirectWithAppBridgeHeaders(params, shop) {
9
+ const {
10
+ config
11
+ } = params;
12
+ const redirectUri = `${config.appUrl}${config.auth.path}?shop=${shop}`;
13
+ throw new Response(undefined, {
14
+ status: 401,
15
+ statusText: 'Unauthorized',
16
+ headers: getAppBridgeHeaders(params, redirectUri, shop)
17
+ });
18
+ }
19
+ function getAppBridgeHeaders(params, url, shop) {
20
+ const {
21
+ config
22
+ } = params;
23
+ const headers = new Headers({
24
+ [_const.REAUTH_URL_HEADER]: url
25
+ });
26
+ addResponseHeaders.addResponseHeaders(headers, config.isEmbeddedApp, shop);
27
+ return headers;
28
+ }
29
+
30
+ exports.getAppBridgeHeaders = getAppBridgeHeaders;
31
+ exports.redirectWithAppBridgeHeaders = redirectWithAppBridgeHeaders;
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var serverRuntime = require('@remix-run/server-runtime');
6
+
7
+ function redirectWithExitIframe(params, request, shop) {
8
+ const {
9
+ api,
10
+ config
11
+ } = params;
12
+ const url = new URL(request.url);
13
+ const queryParams = url.searchParams;
14
+ const host = api.utils.sanitizeHost(queryParams.get('host'));
15
+ queryParams.set('shop', shop);
16
+ queryParams.set('exitIframe', `${config.auth.path}?shop=${shop}`);
17
+ if (host) {
18
+ queryParams.set('host', host);
19
+ }
20
+ throw serverRuntime.redirect(`${config.auth.exitIframePath}?${queryParams.toString()}`);
21
+ }
22
+
23
+ exports.redirectWithExitIframe = redirectWithExitIframe;
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var isbot = require('isbot');
6
+
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
+
9
+ var isbot__default = /*#__PURE__*/_interopDefaultLegacy(isbot);
10
+
11
+ function rejectBotRequest({
12
+ logger
13
+ }, request) {
14
+ if (isbot__default["default"](request.headers.get('User-Agent'))) {
15
+ logger.debug('Request is from a bot, skipping auth');
16
+ throw new Response(undefined, {
17
+ status: 410,
18
+ statusText: 'Gone'
19
+ });
20
+ }
21
+ }
22
+
23
+ exports.rejectBotRequest = rejectBotRequest;
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ async function validateSessionToken({
6
+ api,
7
+ logger
8
+ }, token) {
9
+ logger.debug('Validating session token');
10
+ try {
11
+ const payload = await api.session.decodeSessionToken(token);
12
+ logger.debug('Session token is valid', {
13
+ payload: JSON.stringify(payload)
14
+ });
15
+ return payload;
16
+ } catch (error) {
17
+ logger.debug(`Failed to validate session token: ${error.message}`);
18
+ throw new Response(undefined, {
19
+ status: 401,
20
+ statusText: 'Unauthorized'
21
+ });
22
+ }
23
+ }
24
+
25
+ exports.validateSessionToken = validateSessionToken;
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var serverRuntime = require('@remix-run/server-runtime');
6
+ var types = require('../../types.js');
7
+
8
+ function loginFactory(params) {
9
+ const {
10
+ api,
11
+ config,
12
+ logger
13
+ } = params;
14
+ return async function login(request) {
15
+ if (!config.canUseLoginForm) {
16
+ logger.debug('Login endpoint not available for Shopify Admin custom apps');
17
+ throw new Response(undefined, {
18
+ status: 404
19
+ });
20
+ }
21
+ const url = new URL(request.url);
22
+ const shopParam = url.searchParams.get('shop');
23
+ if (request.method === 'GET' && !shopParam) {
24
+ return {};
25
+ }
26
+ const shop = shopParam || (await request.formData()).get('shop');
27
+ if (!shop) {
28
+ logger.debug('Missing shop parameter', {
29
+ shop
30
+ });
31
+ return {
32
+ shop: types.LoginErrorType.MissingShop
33
+ };
34
+ }
35
+ const shopWithoutProtocol = shop.replace(/^https?:\/\//, '');
36
+ const shopWithDomain = (shop === null || shop === void 0 ? void 0 : shop.indexOf('.')) === -1 ? `${shopWithoutProtocol}.myshopify.com` : shopWithoutProtocol;
37
+ const sanitizedShop = api.utils.sanitizeShop(shopWithDomain);
38
+ if (!sanitizedShop) {
39
+ logger.debug('Invalid shop parameter', {
40
+ shop
41
+ });
42
+ return {
43
+ shop: types.LoginErrorType.InvalidShop
44
+ };
45
+ }
46
+ const redirectUrl = `${config.appUrl}${config.auth.path}?shop=${sanitizedShop}`;
47
+ logger.info(`Redirecting login request to ${redirectUrl}`, {
48
+ shop: sanitizedShop
49
+ });
50
+ throw serverRuntime.redirect(redirectUrl);
51
+ };
52
+ }
53
+
54
+ exports.loginFactory = loginFactory;
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ require('@remix-run/server-runtime');
6
+ var validateSessionToken = require('../helpers/validate-session-token.js');
7
+ var getSessionTokenHeader = require('../helpers/get-session-token-header.js');
8
+ var rejectBotRequest = require('../helpers/reject-bot-request.js');
9
+ require('@shopify/shopify-api');
10
+
11
+ function authenticatePublicFactory(params) {
12
+ return async function authenticatePublic(request) {
13
+ const {
14
+ logger
15
+ } = params;
16
+ rejectBotRequest.rejectBotRequest(params, request);
17
+ const sessionTokenHeader = getSessionTokenHeader.getSessionTokenHeader(request);
18
+ logger.info('Authenticating public request');
19
+ if (!sessionTokenHeader) {
20
+ logger.debug('Request did not contain a session token');
21
+ throw new Response(undefined, {
22
+ status: 401,
23
+ statusText: 'Unauthorized'
24
+ });
25
+ }
26
+ return {
27
+ sessionToken: await validateSessionToken.validateSessionToken(params, sessionTokenHeader)
28
+ };
29
+ };
30
+ }
31
+
32
+ exports.authenticatePublicFactory = authenticatePublicFactory;
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ function authenticateWebhookFactory({
6
+ api,
7
+ config,
8
+ logger
9
+ }) {
10
+ return async function authenticate(request) {
11
+ if (request.method !== 'POST') {
12
+ logger.debug('Received a non-POST request for a webhook. Only POST requests are allowed.', {
13
+ url: request.url,
14
+ method: request.method
15
+ });
16
+ throw new Response(undefined, {
17
+ status: 405,
18
+ statusText: 'Method not allowed'
19
+ });
20
+ }
21
+ const rawBody = await request.text();
22
+ const check = await api.webhooks.validate({
23
+ rawBody,
24
+ rawRequest: request
25
+ });
26
+ if (!check.valid) {
27
+ logger.debug('Webhook validation failed', check);
28
+ throw new Response(undefined, {
29
+ status: 400,
30
+ statusText: 'Bad Request'
31
+ });
32
+ }
33
+ const sessionId = api.session.getOfflineId(check.domain);
34
+ const session = await config.sessionStorage.loadSession(sessionId);
35
+ if (!session) {
36
+ logger.debug('No session found for shop', check);
37
+ throw new Response(undefined, {
38
+ status: 404,
39
+ statusText: 'Not found'
40
+ });
41
+ }
42
+ const restClient = new api.clients.Rest({
43
+ session,
44
+ apiVersion: check.apiVersion
45
+ });
46
+ const graphqlClient = new api.clients.Graphql({
47
+ session,
48
+ apiVersion: check.apiVersion
49
+ });
50
+ Object.entries(api.rest).forEach(([name, resource]) => {
51
+ Reflect.set(restClient, name, resource);
52
+ });
53
+ return {
54
+ apiVersion: check.apiVersion,
55
+ shop: check.domain,
56
+ topic: check.topic,
57
+ webhookId: check.webhookId,
58
+ payload: JSON.parse(rawBody),
59
+ session,
60
+ admin: {
61
+ rest: restClient,
62
+ graphql: graphqlClient
63
+ }
64
+ };
65
+ };
66
+ }
67
+
68
+ exports.authenticateWebhookFactory = authenticateWebhookFactory;
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ function registerWebhooksFactory({
6
+ api,
7
+ logger
8
+ }) {
9
+ return async function registerWebhooks({
10
+ session
11
+ }) {
12
+ return api.webhooks.register({
13
+ session
14
+ }).then(response => {
15
+ Object.entries(response).forEach(([topic, topicResults]) => {
16
+ topicResults.forEach(({
17
+ success,
18
+ ...rest
19
+ }) => {
20
+ if (success) {
21
+ logger.debug('Registered webhook', {
22
+ topic,
23
+ shop: session.shop,
24
+ operation: rest.operation
25
+ });
26
+ } else {
27
+ logger.error('Failed to register webhook', {
28
+ topic,
29
+ shop: session.shop,
30
+ result: JSON.stringify(rest.result)
31
+ });
32
+ }
33
+ });
34
+ });
35
+ return response;
36
+ });
37
+ };
38
+ }
39
+
40
+ exports.registerWebhooksFactory = registerWebhooksFactory;
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var shopifyApi = require('@shopify/shopify-api');
6
+ var redirectToAuthPage = require('../auth/helpers/redirect-to-auth-page.js');
7
+ require('@remix-run/server-runtime');
8
+ require('isbot');
9
+
10
+ function cancelBillingFactory(params, request, session) {
11
+ return async function cancelBilling(options) {
12
+ const {
13
+ api,
14
+ logger
15
+ } = params;
16
+ logger.debug('Cancelling billing', {
17
+ shop: session.shop,
18
+ ...options
19
+ });
20
+ try {
21
+ return await api.billing.cancel({
22
+ session,
23
+ subscriptionId: options.subscriptionId,
24
+ isTest: options.isTest,
25
+ prorate: options.prorate
26
+ });
27
+ } catch (error) {
28
+ if (error instanceof shopifyApi.HttpResponseError && error.response.code === 401) {
29
+ logger.debug('API token was invalid, redirecting to OAuth', {
30
+ shop: session.shop
31
+ });
32
+ throw await redirectToAuthPage.redirectToAuthPage(params, request, session.shop);
33
+ } else {
34
+ throw error;
35
+ }
36
+ }
37
+ };
38
+ }
39
+
40
+ exports.cancelBillingFactory = cancelBillingFactory;