@commercetools-backend/express 0.0.0-FEC-157-preview-releases-20241126230331

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) commercetools GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # @commercetools-backend/express
2
+
3
+ <p align="center">
4
+ <a href="https://www.npmjs.com/package/@commercetools-backend/express"><img src="https://badgen.net/npm/v/@commercetools-backend/express" alt="Latest release (latest dist-tag)" /></a> <a href="https://www.npmjs.com/package/@commercetools-backend/express"><img src="https://badgen.net/npm/v/@commercetools-backend/express/next" alt="Latest release (next dist-tag)" /></a> <a href="https://bundlephobia.com/result?p=@commercetools-backend/express"><img src="https://badgen.net/bundlephobia/minzip/@commercetools-backend/express" alt="Minified + GZipped size" /></a> <a href="https://github.com/commercetools/merchant-center-application-kit/blob/main/LICENSE"><img src="https://badgen.net/github/license/commercetools/merchant-center-application-kit" alt="GitHub license" /></a>
5
+ </p>
6
+
7
+ Zero-config HTTP server as Express.js to facilitate development.
8
+
9
+ This package is primarily built for HTTP servers used by Custom Applications and it provides a set of components to facilitate the development of the HTTP server.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ $ npm install --save @commercetools-backend/express
15
+ ```
16
+
17
+ Check out the [documentation](https://docs.commercetools.com/merchant-center-customizations/concepts/integrate-with-your-own-api) for more information.
@@ -0,0 +1,2 @@
1
+ export * from "./declarations/src/index";
2
+ //# sourceMappingURL=commercetools-backend-express.cjs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commercetools-backend-express.cjs.d.ts","sourceRoot":"","sources":["./declarations/src/index.d.ts"],"names":[],"mappings":"AAAA"}
@@ -0,0 +1,291 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty');
6
+ var _URL = require('@babel/runtime-corejs3/core-js-stable/url');
7
+ var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
8
+ var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
9
+ var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
10
+ var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
11
+ var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
12
+ var _Object$getOwnPropertyDescriptor = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor');
13
+ var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each');
14
+ var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors');
15
+ var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties');
16
+ var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property');
17
+ var expressJwt = require('express-jwt');
18
+ var jwksRsa = require('jwks-rsa');
19
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
20
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
21
+ var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
22
+ var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array');
23
+
24
+ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
25
+
26
+ var _URL__default = /*#__PURE__*/_interopDefault(_URL);
27
+ var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
28
+ var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
29
+ var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
30
+ var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
31
+ var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
32
+ var _Object$getOwnPropertyDescriptor__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptor);
33
+ var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty);
34
+ var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptors);
35
+ var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties);
36
+ var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty);
37
+ var jwksRsa__default = /*#__PURE__*/_interopDefault(jwksRsa);
38
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
39
+ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
40
+ var _Array$isArray__default = /*#__PURE__*/_interopDefault(_Array$isArray);
41
+
42
+ const CLOUD_IDENTIFIERS = {
43
+ GCP_AU: 'gcp-au',
44
+ GCP_EU: 'gcp-eu',
45
+ GCP_US: 'gcp-us',
46
+ AWS_EU: 'aws-eu',
47
+ AWS_US: 'aws-us',
48
+ /**
49
+ * @deprecated: use `AWS_EU` instead
50
+ */
51
+ AWS_FRA: 'aws-fra',
52
+ /**
53
+ * @deprecated: use `AWS_US` instead
54
+ */
55
+ AWS_OHIO: 'aws-ohio',
56
+ /**
57
+ * @deprecated: not supported anymore
58
+ */
59
+ AWS_CN: 'aws-cn',
60
+ AZURE_US: 'azure-us',
61
+ AZURE_EU: 'azure-eu'
62
+ };
63
+ const MC_API_URLS = {
64
+ GCP_AU: 'https://mc-api.australia-southeast1.gcp.commercetools.com',
65
+ GCP_EU: 'https://mc-api.europe-west1.gcp.commercetools.com',
66
+ GCP_US: 'https://mc-api.us-central1.gcp.commercetools.com',
67
+ AWS_EU: 'https://mc-api.eu-central-1.aws.commercetools.com',
68
+ AWS_US: 'https://mc-api.us-east-2.aws.commercetools.com',
69
+ /**
70
+ * @deprecated: use `AWS_EU` instead
71
+ */
72
+ AWS_FRA: 'https://mc-api.eu-central-1.aws.commercetools.com',
73
+ /**
74
+ * @deprecated: use `AWS_US` instead
75
+ */
76
+ AWS_OHIO: 'https://mc-api.us-east-2.aws.commercetools.com',
77
+ /**
78
+ * @deprecated: not supported anymore
79
+ */
80
+ AWS_CN: 'https://mc-api.cn-northwest-1.aws.commercetools.cn',
81
+ AZURE_US: 'https://mc-api.eastus.azure.commercetools.com',
82
+ AZURE_EU: 'https://mc-api.germanywestcentral.azure.commercetools.com'
83
+ };
84
+ const MC_API_PROXY_HEADERS = {
85
+ FORWARD_TO_VERSION: 'x-mc-api-forward-to-version',
86
+ CLOUD_IDENTIFIER: 'x-mc-api-cloud-identifier'
87
+ };
88
+
89
+ const getHeaderByCaseInsensitiveKey = (headers, headerKey) => {
90
+ var _context;
91
+ const matchingHeader = _findInstanceProperty__default["default"](_context = _Object$entries__default["default"](headers)).call(_context, _ref => {
92
+ let _ref2 = _slicedToArray(_ref, 1),
93
+ key = _ref2[0];
94
+ return headerKey.toLowerCase() === key.toLowerCase();
95
+ });
96
+ if (matchingHeader && matchingHeader.length > 0) {
97
+ const _matchingHeader = _slicedToArray(matchingHeader, 2),
98
+ headerValue = _matchingHeader[1];
99
+ return _Array$isArray__default["default"](headerValue) ? headerValue[0] : headerValue;
100
+ }
101
+ return undefined;
102
+ };
103
+ const getFirstHeaderValueOrThrow = (headers, headerKey, errorMessage) => {
104
+ const headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
105
+ if (!headerValue) {
106
+ throw new Error(errorMessage);
107
+ }
108
+ return headerValue;
109
+ };
110
+
111
+ function ownKeys(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; }
112
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
113
+ const decodedTokenKey = 'decoded_token';
114
+ // Assign a session object to the request object.
115
+ const writeSessionContext = request => {
116
+ const decodedToken = request[decodedTokenKey];
117
+ if (decodedToken) {
118
+ const publicClaimForProjectKey = `${decodedToken.iss}/claims/project_key`;
119
+ const publicClaimForUserPermissionsKey = `${decodedToken.iss}/claims/user_permissions`;
120
+ request.session = {
121
+ userId: decodedToken.sub,
122
+ projectKey: decodedToken[publicClaimForProjectKey]
123
+ };
124
+ const userPermissions = decodedToken[publicClaimForUserPermissionsKey];
125
+ if (Boolean(userPermissions?.length)) {
126
+ request.session.userPermissions = userPermissions;
127
+ }
128
+ }
129
+
130
+ // Remove the field used by the JWT middleware.
131
+ delete request.decoded_token;
132
+ };
133
+
134
+ // Given a cloud identifier, try to map it to one of the supported
135
+ // environments and return the MC API URL for that environment.
136
+ // The URL points to the new hostnames.
137
+ // https://docs.commercetools.com/merchant-center-customizations/concepts/merchant-center-api#hostnames
138
+ const mapCloudIdentifierToIssuer = issuer => {
139
+ switch (issuer) {
140
+ case CLOUD_IDENTIFIERS.GCP_AU:
141
+ return MC_API_URLS.GCP_AU;
142
+ case CLOUD_IDENTIFIERS.GCP_EU:
143
+ return MC_API_URLS.GCP_EU;
144
+ case CLOUD_IDENTIFIERS.GCP_US:
145
+ return MC_API_URLS.GCP_US;
146
+ case CLOUD_IDENTIFIERS.AWS_EU:
147
+ case CLOUD_IDENTIFIERS.AWS_FRA:
148
+ return MC_API_URLS.AWS_EU;
149
+ case CLOUD_IDENTIFIERS.AWS_US:
150
+ case CLOUD_IDENTIFIERS.AWS_OHIO:
151
+ return MC_API_URLS.AWS_US;
152
+ // TODO: remove it
153
+ case CLOUD_IDENTIFIERS.AWS_CN:
154
+ return MC_API_URLS.AWS_CN;
155
+ case CLOUD_IDENTIFIERS.AZURE_US:
156
+ return MC_API_URLS.AZURE_US;
157
+ case CLOUD_IDENTIFIERS.AZURE_EU:
158
+ return MC_API_URLS.AZURE_EU;
159
+ default:
160
+ return undefined;
161
+ }
162
+ };
163
+ // Given a cloud identifier, try to map it to a legacy hostname.
164
+ // This is for backwards compatibility.
165
+ const mapToLegacyIssuer = cloudIdentifier => {
166
+ switch (cloudIdentifier) {
167
+ case CLOUD_IDENTIFIERS.GCP_EU:
168
+ return 'https://mc-api.commercetools.com';
169
+ case CLOUD_IDENTIFIERS.GCP_US:
170
+ return 'https://mc-api.commercetools.co';
171
+ default:
172
+ return undefined;
173
+ }
174
+ };
175
+ // Verifies that the issuer is a valid URL.
176
+ const throwIfIssuerIsNotAValidUrl = issuer => {
177
+ try {
178
+ new _URL__default["default"](issuer);
179
+ } catch (error) {
180
+ throw new Error(`Invalid issuer URL "${issuer}". Expected a valid URL to the Merchant Center API Gateway, or a cloud identifier to one of the available cloud regions. See https://docs.commercetools.com/merchant-center-customizations/concepts/merchant-center-api#hostnames.`);
181
+ }
182
+ };
183
+ // Validates required option values.
184
+ const validateRequiredValues = options => {
185
+ if (!options.audience) {
186
+ throw new Error(`Missing required option "audience"`);
187
+ }
188
+ if (!options.issuer) {
189
+ throw new Error(`Missing required option "issuer"`);
190
+ }
191
+ };
192
+ // Attempt to parse the given issuer. If the value is a cloud identifier, it will
193
+ // be mapped to one of the supported values. If not, we assume the value is a valid URL.
194
+ const getConfiguredDefaultIssuer = options => {
195
+ const issuer = mapCloudIdentifierToIssuer(options.issuer);
196
+ if (!issuer) {
197
+ throwIfIssuerIsNotAValidUrl(options.issuer);
198
+ return options.issuer;
199
+ }
200
+ return issuer;
201
+ };
202
+
203
+ // Construct the audience from the given option + the request path.
204
+ // If the request path is `/`, do not append it to the audience, otherwise
205
+ // the token validation might fail because of mismatching audiences.
206
+ const getConfiguredAudience = (options, requestPath) => {
207
+ // remove the trailing slash
208
+ const url = new _URL__default["default"](`${options.audience.replace(/\/?$/, '')}${requestPath}`);
209
+ switch (options.audiencePolicy) {
210
+ case 'forward-url-origin':
211
+ return url.origin;
212
+ default:
213
+ {
214
+ if (requestPath === '/') {
215
+ return url.origin;
216
+ }
217
+ return `${url.origin}${url.pathname}`;
218
+ }
219
+ }
220
+ };
221
+ function createSessionAuthVerifier(options) {
222
+ validateRequiredValues(options);
223
+ const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
224
+
225
+ // Returns an async HTTP handler.
226
+ return async (request, response) => {
227
+ // Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
228
+ const cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, `Missing "X-MC-API-Cloud-Identifier" header.`);
229
+ let issuer = options.inferIssuer && cloudIdentifierHeader ? mapCloudIdentifierToIssuer(cloudIdentifierHeader) ?? configuredDefaultIssuer : configuredDefaultIssuer;
230
+
231
+ // Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
232
+ // The version should be sent by the client making the request, to use the features of v2.
233
+ const proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, `Missing "X-MC-API-Forward-To-Version" header.`);
234
+ if (proxyForwardVersion === 'v1') {
235
+ // Fall back to legacy issuer domains
236
+ issuer = mapToLegacyIssuer(cloudIdentifierHeader) ?? issuer;
237
+ }
238
+ const requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : request.originalUrl ?? request.url;
239
+ if (!requestUrlPath || !_startsWithInstanceProperty__default["default"](requestUrlPath).call(requestUrlPath, '/')) {
240
+ throw new Error(`Invalid request URI path "${requestUrlPath}". Please make sure that the "request" object has either a property "originalUrl" or "url". If not, you should implement the "getRequestUrl" function and make sure to return a valid URI path value starting with "/". More info at https://docs.commercetools.com/merchant-center-customizations/concepts/integrate-with-your-own-api#validating-the-json-web-token`);
241
+ }
242
+ const audience = getConfiguredAudience(options, requestUrlPath);
243
+ return new _Promise__default["default"]((resolve, reject) => {
244
+ expressJwt.expressjwt({
245
+ // Dynamically provide a signing key based on the kid in the header
246
+ // and the singing keys provided by the JWKS endpoint
247
+ secret: jwksRsa__default["default"].expressJwtSecret(_objectSpread(_objectSpread({
248
+ // Default options
249
+ cache: true,
250
+ rateLimit: true,
251
+ jwksRequestsPerMinute: 5
252
+ }, options.jwks || {}), {}, {
253
+ // This should be set by the middleware, no matter what.
254
+ jwksUri: `${issuer}/.well-known/jwks.json`
255
+ })),
256
+ requestProperty: decodedTokenKey,
257
+ // Validate the audience and the issuer.
258
+ audience,
259
+ issuer,
260
+ algorithms: ['RS256']
261
+ // @ts-ignore: the middleware expects an Express.js Request/Response objects
262
+ })(request, response ?? {}, error => {
263
+ if (error) {
264
+ reject(error);
265
+ } else {
266
+ // @ts-ignore: the Request object does not know about some additional fields
267
+ // like `decoded_token` and `session`
268
+ writeSessionContext(request);
269
+ resolve();
270
+ }
271
+ });
272
+ });
273
+ };
274
+ }
275
+
276
+ function createSessionMiddleware(options) {
277
+ const sessionAuthVerifier = createSessionAuthVerifier(options);
278
+ return async (request, response, next) => {
279
+ try {
280
+ await sessionAuthVerifier(request, response);
281
+ next();
282
+ } catch (error) {
283
+ next(error);
284
+ }
285
+ };
286
+ }
287
+
288
+ exports.CLOUD_IDENTIFIERS = CLOUD_IDENTIFIERS;
289
+ exports.MC_API_URLS = MC_API_URLS;
290
+ exports.createSessionAuthVerifier = createSessionAuthVerifier;
291
+ exports.createSessionMiddleware = createSessionMiddleware;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === "production") {
4
+ module.exports = require("./commercetools-backend-express.cjs.prod.js");
5
+ } else {
6
+ module.exports = require("./commercetools-backend-express.cjs.dev.js");
7
+ }
@@ -0,0 +1,291 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty');
6
+ var _URL = require('@babel/runtime-corejs3/core-js-stable/url');
7
+ var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
8
+ var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
9
+ var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
10
+ var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
11
+ var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
12
+ var _Object$getOwnPropertyDescriptor = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor');
13
+ var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each');
14
+ var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors');
15
+ var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties');
16
+ var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property');
17
+ var expressJwt = require('express-jwt');
18
+ var jwksRsa = require('jwks-rsa');
19
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
20
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
21
+ var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
22
+ var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array');
23
+
24
+ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
25
+
26
+ var _URL__default = /*#__PURE__*/_interopDefault(_URL);
27
+ var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
28
+ var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
29
+ var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
30
+ var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
31
+ var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
32
+ var _Object$getOwnPropertyDescriptor__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptor);
33
+ var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty);
34
+ var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptors);
35
+ var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties);
36
+ var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty);
37
+ var jwksRsa__default = /*#__PURE__*/_interopDefault(jwksRsa);
38
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
39
+ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
40
+ var _Array$isArray__default = /*#__PURE__*/_interopDefault(_Array$isArray);
41
+
42
+ const CLOUD_IDENTIFIERS = {
43
+ GCP_AU: 'gcp-au',
44
+ GCP_EU: 'gcp-eu',
45
+ GCP_US: 'gcp-us',
46
+ AWS_EU: 'aws-eu',
47
+ AWS_US: 'aws-us',
48
+ /**
49
+ * @deprecated: use `AWS_EU` instead
50
+ */
51
+ AWS_FRA: 'aws-fra',
52
+ /**
53
+ * @deprecated: use `AWS_US` instead
54
+ */
55
+ AWS_OHIO: 'aws-ohio',
56
+ /**
57
+ * @deprecated: not supported anymore
58
+ */
59
+ AWS_CN: 'aws-cn',
60
+ AZURE_US: 'azure-us',
61
+ AZURE_EU: 'azure-eu'
62
+ };
63
+ const MC_API_URLS = {
64
+ GCP_AU: 'https://mc-api.australia-southeast1.gcp.commercetools.com',
65
+ GCP_EU: 'https://mc-api.europe-west1.gcp.commercetools.com',
66
+ GCP_US: 'https://mc-api.us-central1.gcp.commercetools.com',
67
+ AWS_EU: 'https://mc-api.eu-central-1.aws.commercetools.com',
68
+ AWS_US: 'https://mc-api.us-east-2.aws.commercetools.com',
69
+ /**
70
+ * @deprecated: use `AWS_EU` instead
71
+ */
72
+ AWS_FRA: 'https://mc-api.eu-central-1.aws.commercetools.com',
73
+ /**
74
+ * @deprecated: use `AWS_US` instead
75
+ */
76
+ AWS_OHIO: 'https://mc-api.us-east-2.aws.commercetools.com',
77
+ /**
78
+ * @deprecated: not supported anymore
79
+ */
80
+ AWS_CN: 'https://mc-api.cn-northwest-1.aws.commercetools.cn',
81
+ AZURE_US: 'https://mc-api.eastus.azure.commercetools.com',
82
+ AZURE_EU: 'https://mc-api.germanywestcentral.azure.commercetools.com'
83
+ };
84
+ const MC_API_PROXY_HEADERS = {
85
+ FORWARD_TO_VERSION: 'x-mc-api-forward-to-version',
86
+ CLOUD_IDENTIFIER: 'x-mc-api-cloud-identifier'
87
+ };
88
+
89
+ const getHeaderByCaseInsensitiveKey = (headers, headerKey) => {
90
+ var _context;
91
+ const matchingHeader = _findInstanceProperty__default["default"](_context = _Object$entries__default["default"](headers)).call(_context, _ref => {
92
+ let _ref2 = _slicedToArray(_ref, 1),
93
+ key = _ref2[0];
94
+ return headerKey.toLowerCase() === key.toLowerCase();
95
+ });
96
+ if (matchingHeader && matchingHeader.length > 0) {
97
+ const _matchingHeader = _slicedToArray(matchingHeader, 2),
98
+ headerValue = _matchingHeader[1];
99
+ return _Array$isArray__default["default"](headerValue) ? headerValue[0] : headerValue;
100
+ }
101
+ return undefined;
102
+ };
103
+ const getFirstHeaderValueOrThrow = (headers, headerKey, errorMessage) => {
104
+ const headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
105
+ if (!headerValue) {
106
+ throw new Error(errorMessage);
107
+ }
108
+ return headerValue;
109
+ };
110
+
111
+ function ownKeys(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; }
112
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
113
+ const decodedTokenKey = 'decoded_token';
114
+ // Assign a session object to the request object.
115
+ const writeSessionContext = request => {
116
+ const decodedToken = request[decodedTokenKey];
117
+ if (decodedToken) {
118
+ const publicClaimForProjectKey = `${decodedToken.iss}/claims/project_key`;
119
+ const publicClaimForUserPermissionsKey = `${decodedToken.iss}/claims/user_permissions`;
120
+ request.session = {
121
+ userId: decodedToken.sub,
122
+ projectKey: decodedToken[publicClaimForProjectKey]
123
+ };
124
+ const userPermissions = decodedToken[publicClaimForUserPermissionsKey];
125
+ if (Boolean(userPermissions?.length)) {
126
+ request.session.userPermissions = userPermissions;
127
+ }
128
+ }
129
+
130
+ // Remove the field used by the JWT middleware.
131
+ delete request.decoded_token;
132
+ };
133
+
134
+ // Given a cloud identifier, try to map it to one of the supported
135
+ // environments and return the MC API URL for that environment.
136
+ // The URL points to the new hostnames.
137
+ // https://docs.commercetools.com/merchant-center-customizations/concepts/merchant-center-api#hostnames
138
+ const mapCloudIdentifierToIssuer = issuer => {
139
+ switch (issuer) {
140
+ case CLOUD_IDENTIFIERS.GCP_AU:
141
+ return MC_API_URLS.GCP_AU;
142
+ case CLOUD_IDENTIFIERS.GCP_EU:
143
+ return MC_API_URLS.GCP_EU;
144
+ case CLOUD_IDENTIFIERS.GCP_US:
145
+ return MC_API_URLS.GCP_US;
146
+ case CLOUD_IDENTIFIERS.AWS_EU:
147
+ case CLOUD_IDENTIFIERS.AWS_FRA:
148
+ return MC_API_URLS.AWS_EU;
149
+ case CLOUD_IDENTIFIERS.AWS_US:
150
+ case CLOUD_IDENTIFIERS.AWS_OHIO:
151
+ return MC_API_URLS.AWS_US;
152
+ // TODO: remove it
153
+ case CLOUD_IDENTIFIERS.AWS_CN:
154
+ return MC_API_URLS.AWS_CN;
155
+ case CLOUD_IDENTIFIERS.AZURE_US:
156
+ return MC_API_URLS.AZURE_US;
157
+ case CLOUD_IDENTIFIERS.AZURE_EU:
158
+ return MC_API_URLS.AZURE_EU;
159
+ default:
160
+ return undefined;
161
+ }
162
+ };
163
+ // Given a cloud identifier, try to map it to a legacy hostname.
164
+ // This is for backwards compatibility.
165
+ const mapToLegacyIssuer = cloudIdentifier => {
166
+ switch (cloudIdentifier) {
167
+ case CLOUD_IDENTIFIERS.GCP_EU:
168
+ return 'https://mc-api.commercetools.com';
169
+ case CLOUD_IDENTIFIERS.GCP_US:
170
+ return 'https://mc-api.commercetools.co';
171
+ default:
172
+ return undefined;
173
+ }
174
+ };
175
+ // Verifies that the issuer is a valid URL.
176
+ const throwIfIssuerIsNotAValidUrl = issuer => {
177
+ try {
178
+ new _URL__default["default"](issuer);
179
+ } catch (error) {
180
+ throw new Error(`Invalid issuer URL "${issuer}". Expected a valid URL to the Merchant Center API Gateway, or a cloud identifier to one of the available cloud regions. See https://docs.commercetools.com/merchant-center-customizations/concepts/merchant-center-api#hostnames.`);
181
+ }
182
+ };
183
+ // Validates required option values.
184
+ const validateRequiredValues = options => {
185
+ if (!options.audience) {
186
+ throw new Error(`Missing required option "audience"`);
187
+ }
188
+ if (!options.issuer) {
189
+ throw new Error(`Missing required option "issuer"`);
190
+ }
191
+ };
192
+ // Attempt to parse the given issuer. If the value is a cloud identifier, it will
193
+ // be mapped to one of the supported values. If not, we assume the value is a valid URL.
194
+ const getConfiguredDefaultIssuer = options => {
195
+ const issuer = mapCloudIdentifierToIssuer(options.issuer);
196
+ if (!issuer) {
197
+ throwIfIssuerIsNotAValidUrl(options.issuer);
198
+ return options.issuer;
199
+ }
200
+ return issuer;
201
+ };
202
+
203
+ // Construct the audience from the given option + the request path.
204
+ // If the request path is `/`, do not append it to the audience, otherwise
205
+ // the token validation might fail because of mismatching audiences.
206
+ const getConfiguredAudience = (options, requestPath) => {
207
+ // remove the trailing slash
208
+ const url = new _URL__default["default"](`${options.audience.replace(/\/?$/, '')}${requestPath}`);
209
+ switch (options.audiencePolicy) {
210
+ case 'forward-url-origin':
211
+ return url.origin;
212
+ default:
213
+ {
214
+ if (requestPath === '/') {
215
+ return url.origin;
216
+ }
217
+ return `${url.origin}${url.pathname}`;
218
+ }
219
+ }
220
+ };
221
+ function createSessionAuthVerifier(options) {
222
+ validateRequiredValues(options);
223
+ const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
224
+
225
+ // Returns an async HTTP handler.
226
+ return async (request, response) => {
227
+ // Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
228
+ const cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, `Missing "X-MC-API-Cloud-Identifier" header.`);
229
+ let issuer = options.inferIssuer && cloudIdentifierHeader ? mapCloudIdentifierToIssuer(cloudIdentifierHeader) ?? configuredDefaultIssuer : configuredDefaultIssuer;
230
+
231
+ // Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
232
+ // The version should be sent by the client making the request, to use the features of v2.
233
+ const proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, `Missing "X-MC-API-Forward-To-Version" header.`);
234
+ if (proxyForwardVersion === 'v1') {
235
+ // Fall back to legacy issuer domains
236
+ issuer = mapToLegacyIssuer(cloudIdentifierHeader) ?? issuer;
237
+ }
238
+ const requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : request.originalUrl ?? request.url;
239
+ if (!requestUrlPath || !_startsWithInstanceProperty__default["default"](requestUrlPath).call(requestUrlPath, '/')) {
240
+ throw new Error(`Invalid request URI path "${requestUrlPath}". Please make sure that the "request" object has either a property "originalUrl" or "url". If not, you should implement the "getRequestUrl" function and make sure to return a valid URI path value starting with "/". More info at https://docs.commercetools.com/merchant-center-customizations/concepts/integrate-with-your-own-api#validating-the-json-web-token`);
241
+ }
242
+ const audience = getConfiguredAudience(options, requestUrlPath);
243
+ return new _Promise__default["default"]((resolve, reject) => {
244
+ expressJwt.expressjwt({
245
+ // Dynamically provide a signing key based on the kid in the header
246
+ // and the singing keys provided by the JWKS endpoint
247
+ secret: jwksRsa__default["default"].expressJwtSecret(_objectSpread(_objectSpread({
248
+ // Default options
249
+ cache: true,
250
+ rateLimit: true,
251
+ jwksRequestsPerMinute: 5
252
+ }, options.jwks || {}), {}, {
253
+ // This should be set by the middleware, no matter what.
254
+ jwksUri: `${issuer}/.well-known/jwks.json`
255
+ })),
256
+ requestProperty: decodedTokenKey,
257
+ // Validate the audience and the issuer.
258
+ audience,
259
+ issuer,
260
+ algorithms: ['RS256']
261
+ // @ts-ignore: the middleware expects an Express.js Request/Response objects
262
+ })(request, response ?? {}, error => {
263
+ if (error) {
264
+ reject(error);
265
+ } else {
266
+ // @ts-ignore: the Request object does not know about some additional fields
267
+ // like `decoded_token` and `session`
268
+ writeSessionContext(request);
269
+ resolve();
270
+ }
271
+ });
272
+ });
273
+ };
274
+ }
275
+
276
+ function createSessionMiddleware(options) {
277
+ const sessionAuthVerifier = createSessionAuthVerifier(options);
278
+ return async (request, response, next) => {
279
+ try {
280
+ await sessionAuthVerifier(request, response);
281
+ next();
282
+ } catch (error) {
283
+ next(error);
284
+ }
285
+ };
286
+ }
287
+
288
+ exports.CLOUD_IDENTIFIERS = CLOUD_IDENTIFIERS;
289
+ exports.MC_API_URLS = MC_API_URLS;
290
+ exports.createSessionAuthVerifier = createSessionAuthVerifier;
291
+ exports.createSessionMiddleware = createSessionMiddleware;
@@ -0,0 +1,266 @@
1
+ import _defineProperty from '@babel/runtime-corejs3/helpers/esm/defineProperty';
2
+ import _URL from '@babel/runtime-corejs3/core-js-stable/url';
3
+ import _startsWithInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/starts-with';
4
+ import _Promise from '@babel/runtime-corejs3/core-js-stable/promise';
5
+ import _Object$keys from '@babel/runtime-corejs3/core-js-stable/object/keys';
6
+ import _Object$getOwnPropertySymbols from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols';
7
+ import _filterInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/filter';
8
+ import _Object$getOwnPropertyDescriptor from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor';
9
+ import _forEachInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/for-each';
10
+ import _Object$getOwnPropertyDescriptors from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors';
11
+ import _Object$defineProperties from '@babel/runtime-corejs3/core-js-stable/object/define-properties';
12
+ import _Object$defineProperty from '@babel/runtime-corejs3/core-js-stable/object/define-property';
13
+ import { expressjwt } from 'express-jwt';
14
+ import jwksRsa from 'jwks-rsa';
15
+ import _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray';
16
+ import _findInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find';
17
+ import _Object$entries from '@babel/runtime-corejs3/core-js-stable/object/entries';
18
+ import _Array$isArray from '@babel/runtime-corejs3/core-js-stable/array/is-array';
19
+
20
+ const CLOUD_IDENTIFIERS = {
21
+ GCP_AU: 'gcp-au',
22
+ GCP_EU: 'gcp-eu',
23
+ GCP_US: 'gcp-us',
24
+ AWS_EU: 'aws-eu',
25
+ AWS_US: 'aws-us',
26
+ /**
27
+ * @deprecated: use `AWS_EU` instead
28
+ */
29
+ AWS_FRA: 'aws-fra',
30
+ /**
31
+ * @deprecated: use `AWS_US` instead
32
+ */
33
+ AWS_OHIO: 'aws-ohio',
34
+ /**
35
+ * @deprecated: not supported anymore
36
+ */
37
+ AWS_CN: 'aws-cn',
38
+ AZURE_US: 'azure-us',
39
+ AZURE_EU: 'azure-eu'
40
+ };
41
+ const MC_API_URLS = {
42
+ GCP_AU: 'https://mc-api.australia-southeast1.gcp.commercetools.com',
43
+ GCP_EU: 'https://mc-api.europe-west1.gcp.commercetools.com',
44
+ GCP_US: 'https://mc-api.us-central1.gcp.commercetools.com',
45
+ AWS_EU: 'https://mc-api.eu-central-1.aws.commercetools.com',
46
+ AWS_US: 'https://mc-api.us-east-2.aws.commercetools.com',
47
+ /**
48
+ * @deprecated: use `AWS_EU` instead
49
+ */
50
+ AWS_FRA: 'https://mc-api.eu-central-1.aws.commercetools.com',
51
+ /**
52
+ * @deprecated: use `AWS_US` instead
53
+ */
54
+ AWS_OHIO: 'https://mc-api.us-east-2.aws.commercetools.com',
55
+ /**
56
+ * @deprecated: not supported anymore
57
+ */
58
+ AWS_CN: 'https://mc-api.cn-northwest-1.aws.commercetools.cn',
59
+ AZURE_US: 'https://mc-api.eastus.azure.commercetools.com',
60
+ AZURE_EU: 'https://mc-api.germanywestcentral.azure.commercetools.com'
61
+ };
62
+ const MC_API_PROXY_HEADERS = {
63
+ FORWARD_TO_VERSION: 'x-mc-api-forward-to-version',
64
+ CLOUD_IDENTIFIER: 'x-mc-api-cloud-identifier'
65
+ };
66
+
67
+ const getHeaderByCaseInsensitiveKey = (headers, headerKey) => {
68
+ var _context;
69
+ const matchingHeader = _findInstanceProperty(_context = _Object$entries(headers)).call(_context, _ref => {
70
+ let _ref2 = _slicedToArray(_ref, 1),
71
+ key = _ref2[0];
72
+ return headerKey.toLowerCase() === key.toLowerCase();
73
+ });
74
+ if (matchingHeader && matchingHeader.length > 0) {
75
+ const _matchingHeader = _slicedToArray(matchingHeader, 2),
76
+ headerValue = _matchingHeader[1];
77
+ return _Array$isArray(headerValue) ? headerValue[0] : headerValue;
78
+ }
79
+ return undefined;
80
+ };
81
+ const getFirstHeaderValueOrThrow = (headers, headerKey, errorMessage) => {
82
+ const headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
83
+ if (!headerValue) {
84
+ throw new Error(errorMessage);
85
+ }
86
+ return headerValue;
87
+ };
88
+
89
+ function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
90
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
91
+ const decodedTokenKey = 'decoded_token';
92
+ // Assign a session object to the request object.
93
+ const writeSessionContext = request => {
94
+ const decodedToken = request[decodedTokenKey];
95
+ if (decodedToken) {
96
+ const publicClaimForProjectKey = `${decodedToken.iss}/claims/project_key`;
97
+ const publicClaimForUserPermissionsKey = `${decodedToken.iss}/claims/user_permissions`;
98
+ request.session = {
99
+ userId: decodedToken.sub,
100
+ projectKey: decodedToken[publicClaimForProjectKey]
101
+ };
102
+ const userPermissions = decodedToken[publicClaimForUserPermissionsKey];
103
+ if (Boolean(userPermissions?.length)) {
104
+ request.session.userPermissions = userPermissions;
105
+ }
106
+ }
107
+
108
+ // Remove the field used by the JWT middleware.
109
+ delete request.decoded_token;
110
+ };
111
+
112
+ // Given a cloud identifier, try to map it to one of the supported
113
+ // environments and return the MC API URL for that environment.
114
+ // The URL points to the new hostnames.
115
+ // https://docs.commercetools.com/merchant-center-customizations/concepts/merchant-center-api#hostnames
116
+ const mapCloudIdentifierToIssuer = issuer => {
117
+ switch (issuer) {
118
+ case CLOUD_IDENTIFIERS.GCP_AU:
119
+ return MC_API_URLS.GCP_AU;
120
+ case CLOUD_IDENTIFIERS.GCP_EU:
121
+ return MC_API_URLS.GCP_EU;
122
+ case CLOUD_IDENTIFIERS.GCP_US:
123
+ return MC_API_URLS.GCP_US;
124
+ case CLOUD_IDENTIFIERS.AWS_EU:
125
+ case CLOUD_IDENTIFIERS.AWS_FRA:
126
+ return MC_API_URLS.AWS_EU;
127
+ case CLOUD_IDENTIFIERS.AWS_US:
128
+ case CLOUD_IDENTIFIERS.AWS_OHIO:
129
+ return MC_API_URLS.AWS_US;
130
+ // TODO: remove it
131
+ case CLOUD_IDENTIFIERS.AWS_CN:
132
+ return MC_API_URLS.AWS_CN;
133
+ case CLOUD_IDENTIFIERS.AZURE_US:
134
+ return MC_API_URLS.AZURE_US;
135
+ case CLOUD_IDENTIFIERS.AZURE_EU:
136
+ return MC_API_URLS.AZURE_EU;
137
+ default:
138
+ return undefined;
139
+ }
140
+ };
141
+ // Given a cloud identifier, try to map it to a legacy hostname.
142
+ // This is for backwards compatibility.
143
+ const mapToLegacyIssuer = cloudIdentifier => {
144
+ switch (cloudIdentifier) {
145
+ case CLOUD_IDENTIFIERS.GCP_EU:
146
+ return 'https://mc-api.commercetools.com';
147
+ case CLOUD_IDENTIFIERS.GCP_US:
148
+ return 'https://mc-api.commercetools.co';
149
+ default:
150
+ return undefined;
151
+ }
152
+ };
153
+ // Verifies that the issuer is a valid URL.
154
+ const throwIfIssuerIsNotAValidUrl = issuer => {
155
+ try {
156
+ new _URL(issuer);
157
+ } catch (error) {
158
+ throw new Error(`Invalid issuer URL "${issuer}". Expected a valid URL to the Merchant Center API Gateway, or a cloud identifier to one of the available cloud regions. See https://docs.commercetools.com/merchant-center-customizations/concepts/merchant-center-api#hostnames.`);
159
+ }
160
+ };
161
+ // Validates required option values.
162
+ const validateRequiredValues = options => {
163
+ if (!options.audience) {
164
+ throw new Error(`Missing required option "audience"`);
165
+ }
166
+ if (!options.issuer) {
167
+ throw new Error(`Missing required option "issuer"`);
168
+ }
169
+ };
170
+ // Attempt to parse the given issuer. If the value is a cloud identifier, it will
171
+ // be mapped to one of the supported values. If not, we assume the value is a valid URL.
172
+ const getConfiguredDefaultIssuer = options => {
173
+ const issuer = mapCloudIdentifierToIssuer(options.issuer);
174
+ if (!issuer) {
175
+ throwIfIssuerIsNotAValidUrl(options.issuer);
176
+ return options.issuer;
177
+ }
178
+ return issuer;
179
+ };
180
+
181
+ // Construct the audience from the given option + the request path.
182
+ // If the request path is `/`, do not append it to the audience, otherwise
183
+ // the token validation might fail because of mismatching audiences.
184
+ const getConfiguredAudience = (options, requestPath) => {
185
+ // remove the trailing slash
186
+ const url = new _URL(`${options.audience.replace(/\/?$/, '')}${requestPath}`);
187
+ switch (options.audiencePolicy) {
188
+ case 'forward-url-origin':
189
+ return url.origin;
190
+ default:
191
+ {
192
+ if (requestPath === '/') {
193
+ return url.origin;
194
+ }
195
+ return `${url.origin}${url.pathname}`;
196
+ }
197
+ }
198
+ };
199
+ function createSessionAuthVerifier(options) {
200
+ validateRequiredValues(options);
201
+ const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
202
+
203
+ // Returns an async HTTP handler.
204
+ return async (request, response) => {
205
+ // Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
206
+ const cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, `Missing "X-MC-API-Cloud-Identifier" header.`);
207
+ let issuer = options.inferIssuer && cloudIdentifierHeader ? mapCloudIdentifierToIssuer(cloudIdentifierHeader) ?? configuredDefaultIssuer : configuredDefaultIssuer;
208
+
209
+ // Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
210
+ // The version should be sent by the client making the request, to use the features of v2.
211
+ const proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, `Missing "X-MC-API-Forward-To-Version" header.`);
212
+ if (proxyForwardVersion === 'v1') {
213
+ // Fall back to legacy issuer domains
214
+ issuer = mapToLegacyIssuer(cloudIdentifierHeader) ?? issuer;
215
+ }
216
+ const requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : request.originalUrl ?? request.url;
217
+ if (!requestUrlPath || !_startsWithInstanceProperty(requestUrlPath).call(requestUrlPath, '/')) {
218
+ throw new Error(`Invalid request URI path "${requestUrlPath}". Please make sure that the "request" object has either a property "originalUrl" or "url". If not, you should implement the "getRequestUrl" function and make sure to return a valid URI path value starting with "/". More info at https://docs.commercetools.com/merchant-center-customizations/concepts/integrate-with-your-own-api#validating-the-json-web-token`);
219
+ }
220
+ const audience = getConfiguredAudience(options, requestUrlPath);
221
+ return new _Promise((resolve, reject) => {
222
+ expressjwt({
223
+ // Dynamically provide a signing key based on the kid in the header
224
+ // and the singing keys provided by the JWKS endpoint
225
+ secret: jwksRsa.expressJwtSecret(_objectSpread(_objectSpread({
226
+ // Default options
227
+ cache: true,
228
+ rateLimit: true,
229
+ jwksRequestsPerMinute: 5
230
+ }, options.jwks || {}), {}, {
231
+ // This should be set by the middleware, no matter what.
232
+ jwksUri: `${issuer}/.well-known/jwks.json`
233
+ })),
234
+ requestProperty: decodedTokenKey,
235
+ // Validate the audience and the issuer.
236
+ audience,
237
+ issuer,
238
+ algorithms: ['RS256']
239
+ // @ts-ignore: the middleware expects an Express.js Request/Response objects
240
+ })(request, response ?? {}, error => {
241
+ if (error) {
242
+ reject(error);
243
+ } else {
244
+ // @ts-ignore: the Request object does not know about some additional fields
245
+ // like `decoded_token` and `session`
246
+ writeSessionContext(request);
247
+ resolve();
248
+ }
249
+ });
250
+ });
251
+ };
252
+ }
253
+
254
+ function createSessionMiddleware(options) {
255
+ const sessionAuthVerifier = createSessionAuthVerifier(options);
256
+ return async (request, response, next) => {
257
+ try {
258
+ await sessionAuthVerifier(request, response);
259
+ next();
260
+ } catch (error) {
261
+ next(error);
262
+ }
263
+ };
264
+ }
265
+
266
+ export { CLOUD_IDENTIFIERS, MC_API_URLS, createSessionAuthVerifier, createSessionMiddleware };
@@ -0,0 +1,13 @@
1
+ import type { TSessionMiddlewareOptions, TSession, TBaseRequest } from './types';
2
+ type TDecodedJWT = {
3
+ sub: string;
4
+ iss: string;
5
+ [property: string]: string | string[];
6
+ };
7
+ declare const writeSessionContext: <Request extends TBaseRequest>(request: Request & {
8
+ decoded_token?: TDecodedJWT | undefined;
9
+ session?: TSession | undefined;
10
+ }) => void;
11
+ export declare const getConfiguredAudience: <Request extends TBaseRequest>(options: TSessionMiddlewareOptions<Request>, requestPath: string) => string;
12
+ declare function createSessionAuthVerifier<Request extends TBaseRequest>(options: TSessionMiddlewareOptions<Request>): (request: Request, response?: unknown) => Promise<void>;
13
+ export { createSessionAuthVerifier, writeSessionContext };
@@ -0,0 +1,46 @@
1
+ export declare const CLOUD_IDENTIFIERS: {
2
+ readonly GCP_AU: "gcp-au";
3
+ readonly GCP_EU: "gcp-eu";
4
+ readonly GCP_US: "gcp-us";
5
+ readonly AWS_EU: "aws-eu";
6
+ readonly AWS_US: "aws-us";
7
+ /**
8
+ * @deprecated: use `AWS_EU` instead
9
+ */
10
+ readonly AWS_FRA: "aws-fra";
11
+ /**
12
+ * @deprecated: use `AWS_US` instead
13
+ */
14
+ readonly AWS_OHIO: "aws-ohio";
15
+ /**
16
+ * @deprecated: not supported anymore
17
+ */
18
+ readonly AWS_CN: "aws-cn";
19
+ readonly AZURE_US: "azure-us";
20
+ readonly AZURE_EU: "azure-eu";
21
+ };
22
+ export declare const MC_API_URLS: {
23
+ readonly GCP_AU: "https://mc-api.australia-southeast1.gcp.commercetools.com";
24
+ readonly GCP_EU: "https://mc-api.europe-west1.gcp.commercetools.com";
25
+ readonly GCP_US: "https://mc-api.us-central1.gcp.commercetools.com";
26
+ readonly AWS_EU: "https://mc-api.eu-central-1.aws.commercetools.com";
27
+ readonly AWS_US: "https://mc-api.us-east-2.aws.commercetools.com";
28
+ /**
29
+ * @deprecated: use `AWS_EU` instead
30
+ */
31
+ readonly AWS_FRA: "https://mc-api.eu-central-1.aws.commercetools.com";
32
+ /**
33
+ * @deprecated: use `AWS_US` instead
34
+ */
35
+ readonly AWS_OHIO: "https://mc-api.us-east-2.aws.commercetools.com";
36
+ /**
37
+ * @deprecated: not supported anymore
38
+ */
39
+ readonly AWS_CN: "https://mc-api.cn-northwest-1.aws.commercetools.cn";
40
+ readonly AZURE_US: "https://mc-api.eastus.azure.commercetools.com";
41
+ readonly AZURE_EU: "https://mc-api.germanywestcentral.azure.commercetools.com";
42
+ };
43
+ export declare const MC_API_PROXY_HEADERS: {
44
+ readonly FORWARD_TO_VERSION: "x-mc-api-forward-to-version";
45
+ readonly CLOUD_IDENTIFIER: "x-mc-api-cloud-identifier";
46
+ };
@@ -0,0 +1,3 @@
1
+ export { CLOUD_IDENTIFIERS, MC_API_URLS } from './constants';
2
+ export { createSessionAuthVerifier } from './auth';
3
+ export { default as createSessionMiddleware } from './middlewares/session-middleware';
@@ -0,0 +1,4 @@
1
+ import type { Response, NextFunction } from 'express';
2
+ import type { TBaseRequest, TSessionMiddlewareOptions } from '../types';
3
+ declare function createSessionMiddleware<Request extends TBaseRequest>(options: TSessionMiddlewareOptions<Request>): (request: Request, response: Response, next: NextFunction) => Promise<void>;
4
+ export default createSessionMiddleware;
@@ -0,0 +1,52 @@
1
+ import type { ExpressJwtOptions } from 'jwks-rsa';
2
+ import { CLOUD_IDENTIFIERS } from './constants';
3
+ export type TAudience = string;
4
+ export type TIssuer = string;
5
+ export type TCloudIdentifier = (typeof CLOUD_IDENTIFIERS)[keyof typeof CLOUD_IDENTIFIERS];
6
+ export type TAudiencePolicy = 'forward-url-full-path' | 'forward-url-origin';
7
+ export interface TBaseRequest {
8
+ headers: Record<string, string | string[] | undefined>;
9
+ url?: string;
10
+ originalUrl?: string;
11
+ }
12
+ export type TSessionMiddlewareOptions<Request extends TBaseRequest> = {
13
+ /**
14
+ * The public-facing URL used to connect to the server / serverless function.
15
+ * The value should only contain the origin URL (protocol, hostname, port),
16
+ * the request path is inferred from the incoming request.
17
+ */
18
+ audience: TAudience;
19
+ /**
20
+ * How the audience value should be exchanged between the server and the client.
21
+ * By default it uses the full URL (origin + pathname).
22
+ */
23
+ audiencePolicy?: TAudiencePolicy;
24
+ /**
25
+ * The cloud identifier (see `CLOUD_IDENTIFIERS`) that maps to the MC API URL
26
+ * of the related cloud region or the MC API URL.
27
+ */
28
+ issuer: TCloudIdentifier | TIssuer;
29
+ /**
30
+ * Determines whether the issuer should be inferred from the custom request
31
+ * HTTP header `x-mc-api-cloud-identifier` which is sent by the MC API when
32
+ * forwarding the request.
33
+ * This might be useful in case the server is used in multiple regions.
34
+ */
35
+ inferIssuer?: boolean;
36
+ /**
37
+ * Options for the `jwksRsa.expressJwtSecret`
38
+ */
39
+ jwks?: Omit<ExpressJwtOptions, 'jwksUri'>;
40
+ /**
41
+ * By default we assume that the `request` is a Node.js-like object having either
42
+ * an `originalUrl` or `url` properties.
43
+ * If that's not the case (for example in AWS Lambda functions) you need to correctly
44
+ * map the URL (URI path + query string) of the `request`.
45
+ */
46
+ getRequestUrl?: (request: Request) => string;
47
+ };
48
+ export type TSession = {
49
+ userId: string;
50
+ projectKey: string;
51
+ userPermissions?: string[];
52
+ };
@@ -0,0 +1,3 @@
1
+ declare const getHeaderByCaseInsensitiveKey: (headers: Record<string, string | string[] | undefined>, headerKey: string) => string | undefined;
2
+ declare const getFirstHeaderValueOrThrow: (headers: Record<string, string | string[] | undefined>, headerKey: string, errorMessage: string) => string;
3
+ export { getHeaderByCaseInsensitiveKey, getFirstHeaderValueOrThrow };
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@commercetools-backend/express",
3
+ "version": "0.0.0-FEC-157-preview-releases-20241126230331",
4
+ "description": "Zero-config HTTP server as Express.js to facilitate development",
5
+ "bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/commercetools/merchant-center-application-kit.git",
9
+ "directory": "packages-backend/express"
10
+ },
11
+ "homepage": "https://docs.commercetools.com/merchant-center-customizations",
12
+ "keywords": [
13
+ "javascript",
14
+ "nodejs",
15
+ "express",
16
+ "http",
17
+ "server",
18
+ "toolkit"
19
+ ],
20
+ "license": "MIT",
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "main": "dist/commercetools-backend-express.cjs.js",
25
+ "module": "dist/commercetools-backend-express.esm.js",
26
+ "files": [
27
+ "dist",
28
+ "package.json",
29
+ "LICENSE",
30
+ "README.md"
31
+ ],
32
+ "dependencies": {
33
+ "@babel/runtime": "^7.22.15",
34
+ "@babel/runtime-corejs3": "^7.22.15",
35
+ "@types/express": "^4.17.17",
36
+ "@types/node": "^18.17.14",
37
+ "express": "4.20.0",
38
+ "express-jwt": "8.4.1",
39
+ "jwks-rsa": "2.1.5"
40
+ },
41
+ "devDependencies": {
42
+ "@tsconfig/node16": "^16.1.1",
43
+ "@types/jsonwebtoken": "^9.0.2",
44
+ "jose": "2.0.7",
45
+ "msw": "0.49.3"
46
+ }
47
+ }