@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.
- package/LICENSE.md +9 -0
- package/README.md +373 -0
- package/build/cjs/adapters/node/index.js +13 -0
- package/build/cjs/auth/admin/authenticate.js +599 -0
- package/build/cjs/auth/admin/graphql-client.js +57 -0
- package/build/cjs/auth/admin/rest-client.js +124 -0
- package/build/cjs/auth/const.js +17 -0
- package/build/cjs/auth/helpers/add-response-headers.js +69 -0
- package/build/cjs/auth/helpers/begin-auth.js +18 -0
- package/build/cjs/auth/helpers/get-session-token-header.js +10 -0
- package/build/cjs/auth/helpers/handle-client-error.js +37 -0
- package/build/cjs/auth/helpers/redirect-to-auth-page.js +22 -0
- package/build/cjs/auth/helpers/redirect-with-app-bridge-headers.js +31 -0
- package/build/cjs/auth/helpers/redirect-with-exitiframe.js +23 -0
- package/build/cjs/auth/helpers/reject-bot-request.js +23 -0
- package/build/cjs/auth/helpers/validate-session-token.js +25 -0
- package/build/cjs/auth/login/login.js +54 -0
- package/build/cjs/auth/public/authenticate.js +32 -0
- package/build/cjs/auth/webhooks/authenticate.js +68 -0
- package/build/cjs/auth/webhooks/register.js +40 -0
- package/build/cjs/billing/cancel.js +40 -0
- package/build/cjs/billing/request.js +83 -0
- package/build/cjs/billing/require.js +47 -0
- package/build/cjs/index.js +140 -0
- package/build/cjs/override-logger.js +55 -0
- package/build/cjs/types.js +25 -0
- package/build/cjs/version.js +7 -0
- package/build/ts/adapters/node/index.d.ts +2 -0
- package/build/ts/adapters/node/index.d.ts.map +1 -0
- package/build/ts/adapters/node/index.js +10 -0
- package/build/ts/adapters/node/index.js.map +1 -0
- package/build/ts/auth/admin/authenticate.d.ts +32 -0
- package/build/ts/auth/admin/authenticate.d.ts.map +1 -0
- package/build/ts/auth/admin/authenticate.js +571 -0
- package/build/ts/auth/admin/authenticate.js.map +1 -0
- package/build/ts/auth/admin/graphql-client.d.ts +22 -0
- package/build/ts/auth/admin/graphql-client.d.ts.map +1 -0
- package/build/ts/auth/admin/graphql-client.js +53 -0
- package/build/ts/auth/admin/graphql-client.js.map +1 -0
- package/build/ts/auth/admin/rest-client.d.ts +34 -0
- package/build/ts/auth/admin/rest-client.d.ts.map +1 -0
- package/build/ts/auth/admin/rest-client.js +134 -0
- package/build/ts/auth/admin/rest-client.js.map +1 -0
- package/build/ts/auth/admin/types.d.ts +100 -0
- package/build/ts/auth/admin/types.d.ts.map +1 -0
- package/build/ts/auth/admin/types.js +3 -0
- package/build/ts/auth/admin/types.js.map +1 -0
- package/build/ts/auth/const.d.ts +9 -0
- package/build/ts/auth/const.d.ts.map +1 -0
- package/build/ts/auth/const.js +12 -0
- package/build/ts/auth/const.js.map +1 -0
- package/build/ts/auth/helpers/add-response-headers.d.ts +5 -0
- package/build/ts/auth/helpers/add-response-headers.d.ts.map +1 -0
- package/build/ts/auth/helpers/add-response-headers.js +60 -0
- package/build/ts/auth/helpers/add-response-headers.js.map +1 -0
- package/build/ts/auth/helpers/begin-auth.d.ts +3 -0
- package/build/ts/auth/helpers/begin-auth.d.ts.map +1 -0
- package/build/ts/auth/helpers/begin-auth.js +24 -0
- package/build/ts/auth/helpers/begin-auth.js.map +1 -0
- package/build/ts/auth/helpers/get-session-token-header.d.ts +2 -0
- package/build/ts/auth/helpers/get-session-token-header.d.ts.map +1 -0
- package/build/ts/auth/helpers/get-session-token-header.js +9 -0
- package/build/ts/auth/helpers/get-session-token-header.js.map +1 -0
- package/build/ts/auth/helpers/handle-client-error.d.ts +11 -0
- package/build/ts/auth/helpers/handle-client-error.d.ts.map +1 -0
- package/build/ts/auth/helpers/handle-client-error.js +40 -0
- package/build/ts/auth/helpers/handle-client-error.js.map +1 -0
- package/build/ts/auth/helpers/index.d.ts +8 -0
- package/build/ts/auth/helpers/index.d.ts.map +1 -0
- package/build/ts/auth/helpers/index.js +18 -0
- package/build/ts/auth/helpers/index.js.map +1 -0
- package/build/ts/auth/helpers/redirect-to-auth-page.d.ts +3 -0
- package/build/ts/auth/helpers/redirect-to-auth-page.d.ts.map +1 -0
- package/build/ts/auth/helpers/redirect-to-auth-page.js +33 -0
- package/build/ts/auth/helpers/redirect-to-auth-page.js.map +1 -0
- package/build/ts/auth/helpers/redirect-with-app-bridge-headers.d.ts +4 -0
- package/build/ts/auth/helpers/redirect-with-app-bridge-headers.d.ts.map +1 -0
- package/build/ts/auth/helpers/redirect-with-app-bridge-headers.js +24 -0
- package/build/ts/auth/helpers/redirect-with-app-bridge-headers.js.map +1 -0
- package/build/ts/auth/helpers/redirect-with-exitiframe.d.ts +3 -0
- package/build/ts/auth/helpers/redirect-with-exitiframe.d.ts.map +1 -0
- package/build/ts/auth/helpers/redirect-with-exitiframe.js +18 -0
- package/build/ts/auth/helpers/redirect-with-exitiframe.js.map +1 -0
- package/build/ts/auth/helpers/reject-bot-request.d.ts +3 -0
- package/build/ts/auth/helpers/reject-bot-request.d.ts.map +1 -0
- package/build/ts/auth/helpers/reject-bot-request.js +14 -0
- package/build/ts/auth/helpers/reject-bot-request.js.map +1 -0
- package/build/ts/auth/helpers/validate-session-token.d.ts +4 -0
- package/build/ts/auth/helpers/validate-session-token.d.ts.map +1 -0
- package/build/ts/auth/helpers/validate-session-token.js +36 -0
- package/build/ts/auth/helpers/validate-session-token.js.map +1 -0
- package/build/ts/auth/login/login.d.ts +3 -0
- package/build/ts/auth/login/login.d.ts.map +1 -0
- package/build/ts/auth/login/login.js +56 -0
- package/build/ts/auth/login/login.js.map +1 -0
- package/build/ts/auth/public/authenticate.d.ts +4 -0
- package/build/ts/auth/public/authenticate.d.ts.map +1 -0
- package/build/ts/auth/public/authenticate.js +35 -0
- package/build/ts/auth/public/authenticate.js.map +1 -0
- package/build/ts/auth/public/types.d.ts +27 -0
- package/build/ts/auth/public/types.d.ts.map +1 -0
- package/build/ts/auth/public/types.js +3 -0
- package/build/ts/auth/public/types.js.map +1 -0
- package/build/ts/auth/webhooks/authenticate.d.ts +5 -0
- package/build/ts/auth/webhooks/authenticate.d.ts.map +1 -0
- package/build/ts/auth/webhooks/authenticate.js +71 -0
- package/build/ts/auth/webhooks/authenticate.js.map +1 -0
- package/build/ts/auth/webhooks/index.d.ts +2 -0
- package/build/ts/auth/webhooks/index.d.ts.map +1 -0
- package/build/ts/auth/webhooks/index.js +6 -0
- package/build/ts/auth/webhooks/index.js.map +1 -0
- package/build/ts/auth/webhooks/register.d.ts +4 -0
- package/build/ts/auth/webhooks/register.d.ts.map +1 -0
- package/build/ts/auth/webhooks/register.js +39 -0
- package/build/ts/auth/webhooks/register.js.map +1 -0
- package/build/ts/auth/webhooks/types.d.ts +18 -0
- package/build/ts/auth/webhooks/types.d.ts.map +1 -0
- package/build/ts/auth/webhooks/types.js +3 -0
- package/build/ts/auth/webhooks/types.js.map +1 -0
- package/build/ts/billing/cancel.d.ts +5 -0
- package/build/ts/billing/cancel.d.ts.map +1 -0
- package/build/ts/billing/cancel.js +43 -0
- package/build/ts/billing/cancel.js.map +1 -0
- package/build/ts/billing/index.d.ts +4 -0
- package/build/ts/billing/index.d.ts.map +1 -0
- package/build/ts/billing/index.js +10 -0
- package/build/ts/billing/index.js.map +1 -0
- package/build/ts/billing/request.d.ts +6 -0
- package/build/ts/billing/request.d.ts.map +1 -0
- package/build/ts/billing/request.js +84 -0
- package/build/ts/billing/request.js.map +1 -0
- package/build/ts/billing/require.d.ts +6 -0
- package/build/ts/billing/require.d.ts.map +1 -0
- package/build/ts/billing/require.js +55 -0
- package/build/ts/billing/require.js.map +1 -0
- package/build/ts/billing/types.d.ts +228 -0
- package/build/ts/billing/types.d.ts.map +1 -0
- package/build/ts/billing/types.js +3 -0
- package/build/ts/billing/types.js.map +1 -0
- package/build/ts/config-types.d.ts +325 -0
- package/build/ts/config-types.d.ts.map +1 -0
- package/build/ts/config-types.js +3 -0
- package/build/ts/config-types.js.map +1 -0
- package/build/ts/index.d.ts +29 -0
- package/build/ts/index.d.ts.map +1 -0
- package/build/ts/index.js +108 -0
- package/build/ts/index.js.map +1 -0
- package/build/ts/override-logger.d.ts +3 -0
- package/build/ts/override-logger.d.ts.map +1 -0
- package/build/ts/override-logger.js +40 -0
- package/build/ts/override-logger.js.map +1 -0
- package/build/ts/types.d.ts +353 -0
- package/build/ts/types.d.ts.map +1 -0
- package/build/ts/types.js +17 -0
- package/build/ts/types.js.map +1 -0
- package/build/ts/version.d.ts +2 -0
- package/build/ts/version.d.ts.map +1 -0
- package/build/ts/version.js +5 -0
- package/build/ts/version.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- 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;
|