@commercetools-backend/express 21.3.4 → 21.8.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 commercetools GmbH
3
+ Copyright (c) commercetools GmbH
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -14,72 +14,4 @@ This package is primarily built for HTTP servers used by Custom Applications and
14
14
  $ npm install --save @commercetools-backend/express
15
15
  ```
16
16
 
17
- ## Session middleware
18
-
19
- This Express.js middleware should be used to handle the authentication exchange between the server and the `/proxy/forward-to` endpoint of the Merchant Center API Gateway.
20
-
21
- > You can read more about the "Proxy to External API" concepts [here](https://docs.commercetools.com/custom-applications/main-concepts/proxy-to-external-api).
22
-
23
- ```js
24
- const {
25
- createSessionMiddleware,
26
- CLOUD_IDENTIFIERS,
27
- } = require('@commercetools-backend/express');
28
-
29
- app.use(
30
- createSessionMiddleware({
31
- audience: 'https://my-api-server.com',
32
- issuer: CLOUD_IDENTIFIERS.GCP_EU,
33
- })
34
- );
35
- app.use((request, response, next) => {
36
- // `request.session` contains the useful information
37
- });
38
- ```
39
-
40
- ### Middleware options
41
-
42
- - `audience` (_string_): The public-facing URL of your API server. The value should only contain the origin URL (protocol, hostname, port), the request path is inferred from the incoming request.
43
-
44
- - `issuer` (_string_): Either a cloud identifier or a valid URL to the Merchant Center API Gateway. The cloud identifier maps to the Merchant Center API URL of the related [cloud region](https://docs.commercetools.com/custom-applications/concepts/merchant-center-api#cloud-regions).
45
-
46
- - `gcp-au`: `https://mc-api.australia-southeast1.gcp.commercetools.com`
47
- - `gcp-eu`: `https://mc-api.europe-west1.gcp.commercetools.com`
48
- - `gcp-us`: `https://mc-api.us-central1.gcp.commercetools.com`
49
- - `aws-fra`: `https://mc-api.eu-central-1.aws.commercetools.com`
50
- - `aws-ohio`: `https://mc-api.us-east-2.aws.commercetools.com`
51
-
52
- - `inferIssuer` (_boolean_): Determines whether the issuer should be inferred from the custom request HTTP header `x-mc-api-cloud-identifier` which is sent by the Merchant Center API Gateway when forwarding the request. This might be useful in case the server is used in multiple regions.
53
-
54
- - `jwks` (_object_): See options of `jwks-rsa`.
55
-
56
- ### Usage in Serverless Functions
57
-
58
- If your HTTP server runs as a Serverless Function, the Express.js middleware should not be needed. Instead you can use the underlying function that does not require the `next` callback.
59
-
60
- **Example for Google Cloud Functions**
61
-
62
- ```js
63
- const {
64
- createSessionAuthVerifier,
65
- CLOUD_IDENTIFIERS,
66
- } = require('@commercetools-backend/express');
67
-
68
- const sessionAuthVerifier = createSessionAuthVerifier({
69
- audience: 'https://my-api-server.com',
70
- issuer: CLOUD_IDENTIFIERS.GCP_EU,
71
- });
72
-
73
- exports.handler = async function (request, response) {
74
- try {
75
- await sessionAuthVerifier(request, response);
76
- } catch (error) {
77
- response.status(401).send(JSON.stringify({ message: 'Unauthorized' }));
78
- return;
79
- }
80
-
81
- // `request.session` contains the useful information
82
- };
83
- ```
84
-
85
- > The same concept applies for serverless functions in other cloud providers.
17
+ Check out the [documentation](https://docs.commercetools.com/custom-applications/concepts/integrate-with-your-own-api) for more information.
@@ -7,6 +7,7 @@ var _asyncToGenerator = require('@babel/runtime-corejs3/helpers/asyncToGenerator
7
7
  var _regeneratorRuntime = require('@babel/runtime-corejs3/regenerator');
8
8
  var _URL = require('@babel/runtime-corejs3/core-js-stable/url');
9
9
  var _concatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/concat');
10
+ var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
10
11
  var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
11
12
  var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
12
13
  var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
@@ -17,7 +18,10 @@ var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-
17
18
  var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties');
18
19
  var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property');
19
20
  var jwksRsa = require('jwks-rsa');
20
- var expressJwtMiddleware = require('express-jwt');
21
+ var expressJwt = require('express-jwt');
22
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
23
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
24
+ var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
21
25
  var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array');
22
26
 
23
27
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -25,6 +29,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
25
29
  var _regeneratorRuntime__default = /*#__PURE__*/_interopDefault(_regeneratorRuntime);
26
30
  var _URL__default = /*#__PURE__*/_interopDefault(_URL);
27
31
  var _concatInstanceProperty__default = /*#__PURE__*/_interopDefault(_concatInstanceProperty);
32
+ var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
28
33
  var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
29
34
  var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
30
35
  var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
@@ -35,7 +40,8 @@ var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_O
35
40
  var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties);
36
41
  var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty);
37
42
  var jwksRsa__default = /*#__PURE__*/_interopDefault(jwksRsa);
38
- var expressJwtMiddleware__default = /*#__PURE__*/_interopDefault(expressJwtMiddleware);
43
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
44
+ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
39
45
  var _Array$isArray__default = /*#__PURE__*/_interopDefault(_Array$isArray);
40
46
 
41
47
  var CLOUD_IDENTIFIERS = {
@@ -57,12 +63,34 @@ var MC_API_PROXY_HEADERS = {
57
63
  CLOUD_IDENTIFIER: 'x-mc-api-cloud-identifier'
58
64
  };
59
65
 
60
- var getFirstOrThrow = function getFirstOrThrow(value, errorMessage) {
61
- if (!value) {
66
+ var getHeaderByCaseInsensitiveKey = function getHeaderByCaseInsensitiveKey(headers, headerKey) {
67
+ var _context;
68
+
69
+ var matchingHeader = _findInstanceProperty__default["default"](_context = _Object$entries__default["default"](headers)).call(_context, function (_ref) {
70
+ var _ref2 = _slicedToArray(_ref, 1),
71
+ key = _ref2[0];
72
+
73
+ return headerKey.toLowerCase() === key.toLowerCase();
74
+ });
75
+
76
+ if (matchingHeader && matchingHeader.length > 0) {
77
+ var _matchingHeader = _slicedToArray(matchingHeader, 2),
78
+ headerValue = _matchingHeader[1];
79
+
80
+ return _Array$isArray__default["default"](headerValue) ? headerValue[0] : headerValue;
81
+ }
82
+
83
+ return undefined;
84
+ };
85
+
86
+ var getFirstHeaderValueOrThrow = function getFirstHeaderValueOrThrow(headers, headerKey, errorMessage) {
87
+ var headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
88
+
89
+ if (!headerValue) {
62
90
  throw new Error(errorMessage);
63
91
  }
64
92
 
65
- return _Array$isArray__default["default"](value) ? value[0] : value;
93
+ return headerValue;
66
94
  };
67
95
 
68
96
  function ownKeys(object, enumerableOnly) { var keys = _Object$keys__default["default"](object); if (_Object$getOwnPropertySymbols__default["default"]) { var symbols = _Object$getOwnPropertySymbols__default["default"](object); enumerableOnly && (symbols = _filterInstanceProperty__default["default"](symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor__default["default"](object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
@@ -163,16 +191,26 @@ var getConfiguredDefaultIssuer = function getConfiguredDefaultIssuer(options) {
163
191
 
164
192
 
165
193
  var getConfiguredAudience = function getConfiguredAudience(options, requestPath) {
166
- var _context, _context2;
194
+ var _context;
167
195
 
168
196
  // remove the trailing slash
169
197
  var url = new _URL__default["default"](_concatInstanceProperty__default["default"](_context = "".concat(options.audience.replace(/\/?$/, ''))).call(_context, requestPath));
170
198
 
171
- if (requestPath === '/') {
172
- return url.origin;
173
- }
199
+ switch (options.audiencePolicy) {
200
+ case 'forward-url-origin':
201
+ return url.origin;
202
+
203
+ default:
204
+ {
205
+ var _context2;
174
206
 
175
- return _concatInstanceProperty__default["default"](_context2 = "".concat(url.origin)).call(_context2, url.pathname);
207
+ if (requestPath === '/') {
208
+ return url.origin;
209
+ }
210
+
211
+ return _concatInstanceProperty__default["default"](_context2 = "".concat(url.origin)).call(_context2, url.pathname);
212
+ }
213
+ }
176
214
  };
177
215
 
178
216
  function createSessionAuthVerifier(options) {
@@ -190,11 +228,11 @@ function createSessionAuthVerifier(options) {
190
228
  switch (_context3.prev = _context3.next) {
191
229
  case 0:
192
230
  // Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
193
- cloudIdentifierHeader = getFirstOrThrow(request.headers[MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER], "Missing \"X-MC-API-Cloud-Identifier\" header.");
231
+ cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, "Missing \"X-MC-API-Cloud-Identifier\" header.");
194
232
  issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer; // Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
195
233
  // The version should be sent by the client making the request, to use the features of v2.
196
234
 
197
- proxyForwardVersion = getFirstOrThrow(request.headers[MC_API_PROXY_HEADERS.FORWARD_TO_VERSION], "Missing \"X-MC-API-Forward-To-Version\" header.");
235
+ proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, "Missing \"X-MC-API-Forward-To-Version\" header.");
198
236
 
199
237
  if (proxyForwardVersion === 'v1') {
200
238
  // Fall back to legacy issuer domains
@@ -203,17 +241,17 @@ function createSessionAuthVerifier(options) {
203
241
 
204
242
  requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : (_request$originalUrl = request.originalUrl) !== null && _request$originalUrl !== void 0 ? _request$originalUrl : request.url;
205
243
 
206
- if (requestUrlPath) {
244
+ if (!(!requestUrlPath || !_startsWithInstanceProperty__default["default"](requestUrlPath).call(requestUrlPath, '/'))) {
207
245
  _context3.next = 7;
208
246
  break;
209
247
  }
210
248
 
211
- throw new Error('Invalid request URI path. Please make sure that the `request` object has either a property `originalUrl` or `url`. If not, you should implement the `getRequestUrl` function. More info at https://docs.commercetools.com/custom-applications/concepts/integrate-with-your-own-api#validating-the-json-web-token');
249
+ throw new Error("Invalid request URI path \"".concat(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/custom-applications/concepts/integrate-with-your-own-api#validating-the-json-web-token"));
212
250
 
213
251
  case 7:
214
252
  audience = getConfiguredAudience(options, requestUrlPath);
215
253
  return _context3.abrupt("return", new _Promise__default["default"](function (resolve, reject) {
216
- expressJwtMiddleware__default["default"]({
254
+ expressJwt.expressjwt({
217
255
  // Dynamically provide a signing key based on the kid in the header
218
256
  // and the singing keys provided by the JWKS endpoint
219
257
  secret: jwksRsa__default["default"].expressJwtSecret(_objectSpread(_objectSpread({
@@ -7,6 +7,7 @@ var _asyncToGenerator = require('@babel/runtime-corejs3/helpers/asyncToGenerator
7
7
  var _regeneratorRuntime = require('@babel/runtime-corejs3/regenerator');
8
8
  var _URL = require('@babel/runtime-corejs3/core-js-stable/url');
9
9
  var _concatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/concat');
10
+ var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
10
11
  var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
11
12
  var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
12
13
  var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
@@ -17,7 +18,10 @@ var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-
17
18
  var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties');
18
19
  var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property');
19
20
  var jwksRsa = require('jwks-rsa');
20
- var expressJwtMiddleware = require('express-jwt');
21
+ var expressJwt = require('express-jwt');
22
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
23
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
24
+ var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
21
25
  var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array');
22
26
 
23
27
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -25,6 +29,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
25
29
  var _regeneratorRuntime__default = /*#__PURE__*/_interopDefault(_regeneratorRuntime);
26
30
  var _URL__default = /*#__PURE__*/_interopDefault(_URL);
27
31
  var _concatInstanceProperty__default = /*#__PURE__*/_interopDefault(_concatInstanceProperty);
32
+ var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
28
33
  var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
29
34
  var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
30
35
  var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
@@ -35,7 +40,8 @@ var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_O
35
40
  var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties);
36
41
  var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty);
37
42
  var jwksRsa__default = /*#__PURE__*/_interopDefault(jwksRsa);
38
- var expressJwtMiddleware__default = /*#__PURE__*/_interopDefault(expressJwtMiddleware);
43
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
44
+ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
39
45
  var _Array$isArray__default = /*#__PURE__*/_interopDefault(_Array$isArray);
40
46
 
41
47
  var CLOUD_IDENTIFIERS = {
@@ -57,12 +63,34 @@ var MC_API_PROXY_HEADERS = {
57
63
  CLOUD_IDENTIFIER: 'x-mc-api-cloud-identifier'
58
64
  };
59
65
 
60
- var getFirstOrThrow = function getFirstOrThrow(value, errorMessage) {
61
- if (!value) {
66
+ var getHeaderByCaseInsensitiveKey = function getHeaderByCaseInsensitiveKey(headers, headerKey) {
67
+ var _context;
68
+
69
+ var matchingHeader = _findInstanceProperty__default["default"](_context = _Object$entries__default["default"](headers)).call(_context, function (_ref) {
70
+ var _ref2 = _slicedToArray(_ref, 1),
71
+ key = _ref2[0];
72
+
73
+ return headerKey.toLowerCase() === key.toLowerCase();
74
+ });
75
+
76
+ if (matchingHeader && matchingHeader.length > 0) {
77
+ var _matchingHeader = _slicedToArray(matchingHeader, 2),
78
+ headerValue = _matchingHeader[1];
79
+
80
+ return _Array$isArray__default["default"](headerValue) ? headerValue[0] : headerValue;
81
+ }
82
+
83
+ return undefined;
84
+ };
85
+
86
+ var getFirstHeaderValueOrThrow = function getFirstHeaderValueOrThrow(headers, headerKey, errorMessage) {
87
+ var headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
88
+
89
+ if (!headerValue) {
62
90
  throw new Error(errorMessage);
63
91
  }
64
92
 
65
- return _Array$isArray__default["default"](value) ? value[0] : value;
93
+ return headerValue;
66
94
  };
67
95
 
68
96
  function ownKeys(object, enumerableOnly) { var keys = _Object$keys__default["default"](object); if (_Object$getOwnPropertySymbols__default["default"]) { var symbols = _Object$getOwnPropertySymbols__default["default"](object); enumerableOnly && (symbols = _filterInstanceProperty__default["default"](symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor__default["default"](object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
@@ -163,16 +191,26 @@ var getConfiguredDefaultIssuer = function getConfiguredDefaultIssuer(options) {
163
191
 
164
192
 
165
193
  var getConfiguredAudience = function getConfiguredAudience(options, requestPath) {
166
- var _context, _context2;
194
+ var _context;
167
195
 
168
196
  // remove the trailing slash
169
197
  var url = new _URL__default["default"](_concatInstanceProperty__default["default"](_context = "".concat(options.audience.replace(/\/?$/, ''))).call(_context, requestPath));
170
198
 
171
- if (requestPath === '/') {
172
- return url.origin;
173
- }
199
+ switch (options.audiencePolicy) {
200
+ case 'forward-url-origin':
201
+ return url.origin;
202
+
203
+ default:
204
+ {
205
+ var _context2;
174
206
 
175
- return _concatInstanceProperty__default["default"](_context2 = "".concat(url.origin)).call(_context2, url.pathname);
207
+ if (requestPath === '/') {
208
+ return url.origin;
209
+ }
210
+
211
+ return _concatInstanceProperty__default["default"](_context2 = "".concat(url.origin)).call(_context2, url.pathname);
212
+ }
213
+ }
176
214
  };
177
215
 
178
216
  function createSessionAuthVerifier(options) {
@@ -190,11 +228,11 @@ function createSessionAuthVerifier(options) {
190
228
  switch (_context3.prev = _context3.next) {
191
229
  case 0:
192
230
  // Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
193
- cloudIdentifierHeader = getFirstOrThrow(request.headers[MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER], "Missing \"X-MC-API-Cloud-Identifier\" header.");
231
+ cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, "Missing \"X-MC-API-Cloud-Identifier\" header.");
194
232
  issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer; // Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
195
233
  // The version should be sent by the client making the request, to use the features of v2.
196
234
 
197
- proxyForwardVersion = getFirstOrThrow(request.headers[MC_API_PROXY_HEADERS.FORWARD_TO_VERSION], "Missing \"X-MC-API-Forward-To-Version\" header.");
235
+ proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, "Missing \"X-MC-API-Forward-To-Version\" header.");
198
236
 
199
237
  if (proxyForwardVersion === 'v1') {
200
238
  // Fall back to legacy issuer domains
@@ -203,17 +241,17 @@ function createSessionAuthVerifier(options) {
203
241
 
204
242
  requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : (_request$originalUrl = request.originalUrl) !== null && _request$originalUrl !== void 0 ? _request$originalUrl : request.url;
205
243
 
206
- if (requestUrlPath) {
244
+ if (!(!requestUrlPath || !_startsWithInstanceProperty__default["default"](requestUrlPath).call(requestUrlPath, '/'))) {
207
245
  _context3.next = 7;
208
246
  break;
209
247
  }
210
248
 
211
- throw new Error('Invalid request URI path. Please make sure that the `request` object has either a property `originalUrl` or `url`. If not, you should implement the `getRequestUrl` function. More info at https://docs.commercetools.com/custom-applications/concepts/integrate-with-your-own-api#validating-the-json-web-token');
249
+ throw new Error("Invalid request URI path \"".concat(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/custom-applications/concepts/integrate-with-your-own-api#validating-the-json-web-token"));
212
250
 
213
251
  case 7:
214
252
  audience = getConfiguredAudience(options, requestUrlPath);
215
253
  return _context3.abrupt("return", new _Promise__default["default"](function (resolve, reject) {
216
- expressJwtMiddleware__default["default"]({
254
+ expressJwt.expressjwt({
217
255
  // Dynamically provide a signing key based on the kid in the header
218
256
  // and the singing keys provided by the JWKS endpoint
219
257
  secret: jwksRsa__default["default"].expressJwtSecret(_objectSpread(_objectSpread({
@@ -3,6 +3,7 @@ import _asyncToGenerator from '@babel/runtime-corejs3/helpers/esm/asyncToGenerat
3
3
  import _regeneratorRuntime from '@babel/runtime-corejs3/regenerator';
4
4
  import _URL from '@babel/runtime-corejs3/core-js-stable/url';
5
5
  import _concatInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/concat';
6
+ import _startsWithInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/starts-with';
6
7
  import _Promise from '@babel/runtime-corejs3/core-js-stable/promise';
7
8
  import _Object$keys from '@babel/runtime-corejs3/core-js-stable/object/keys';
8
9
  import _Object$getOwnPropertySymbols from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols';
@@ -13,7 +14,10 @@ import _Object$getOwnPropertyDescriptors from '@babel/runtime-corejs3/core-js-st
13
14
  import _Object$defineProperties from '@babel/runtime-corejs3/core-js-stable/object/define-properties';
14
15
  import _Object$defineProperty from '@babel/runtime-corejs3/core-js-stable/object/define-property';
15
16
  import jwksRsa from 'jwks-rsa';
16
- import expressJwtMiddleware from 'express-jwt';
17
+ import { expressjwt } from 'express-jwt';
18
+ import _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray';
19
+ import _findInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find';
20
+ import _Object$entries from '@babel/runtime-corejs3/core-js-stable/object/entries';
17
21
  import _Array$isArray from '@babel/runtime-corejs3/core-js-stable/array/is-array';
18
22
 
19
23
  var CLOUD_IDENTIFIERS = {
@@ -35,12 +39,34 @@ var MC_API_PROXY_HEADERS = {
35
39
  CLOUD_IDENTIFIER: 'x-mc-api-cloud-identifier'
36
40
  };
37
41
 
38
- var getFirstOrThrow = function getFirstOrThrow(value, errorMessage) {
39
- if (!value) {
42
+ var getHeaderByCaseInsensitiveKey = function getHeaderByCaseInsensitiveKey(headers, headerKey) {
43
+ var _context;
44
+
45
+ var matchingHeader = _findInstanceProperty(_context = _Object$entries(headers)).call(_context, function (_ref) {
46
+ var _ref2 = _slicedToArray(_ref, 1),
47
+ key = _ref2[0];
48
+
49
+ return headerKey.toLowerCase() === key.toLowerCase();
50
+ });
51
+
52
+ if (matchingHeader && matchingHeader.length > 0) {
53
+ var _matchingHeader = _slicedToArray(matchingHeader, 2),
54
+ headerValue = _matchingHeader[1];
55
+
56
+ return _Array$isArray(headerValue) ? headerValue[0] : headerValue;
57
+ }
58
+
59
+ return undefined;
60
+ };
61
+
62
+ var getFirstHeaderValueOrThrow = function getFirstHeaderValueOrThrow(headers, headerKey, errorMessage) {
63
+ var headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
64
+
65
+ if (!headerValue) {
40
66
  throw new Error(errorMessage);
41
67
  }
42
68
 
43
- return _Array$isArray(value) ? value[0] : value;
69
+ return headerValue;
44
70
  };
45
71
 
46
72
  function ownKeys(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); enumerableOnly && (symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
@@ -141,16 +167,26 @@ var getConfiguredDefaultIssuer = function getConfiguredDefaultIssuer(options) {
141
167
 
142
168
 
143
169
  var getConfiguredAudience = function getConfiguredAudience(options, requestPath) {
144
- var _context, _context2;
170
+ var _context;
145
171
 
146
172
  // remove the trailing slash
147
173
  var url = new _URL(_concatInstanceProperty(_context = "".concat(options.audience.replace(/\/?$/, ''))).call(_context, requestPath));
148
174
 
149
- if (requestPath === '/') {
150
- return url.origin;
151
- }
175
+ switch (options.audiencePolicy) {
176
+ case 'forward-url-origin':
177
+ return url.origin;
178
+
179
+ default:
180
+ {
181
+ var _context2;
152
182
 
153
- return _concatInstanceProperty(_context2 = "".concat(url.origin)).call(_context2, url.pathname);
183
+ if (requestPath === '/') {
184
+ return url.origin;
185
+ }
186
+
187
+ return _concatInstanceProperty(_context2 = "".concat(url.origin)).call(_context2, url.pathname);
188
+ }
189
+ }
154
190
  };
155
191
 
156
192
  function createSessionAuthVerifier(options) {
@@ -168,11 +204,11 @@ function createSessionAuthVerifier(options) {
168
204
  switch (_context3.prev = _context3.next) {
169
205
  case 0:
170
206
  // Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
171
- cloudIdentifierHeader = getFirstOrThrow(request.headers[MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER], "Missing \"X-MC-API-Cloud-Identifier\" header.");
207
+ cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, "Missing \"X-MC-API-Cloud-Identifier\" header.");
172
208
  issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer; // Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
173
209
  // The version should be sent by the client making the request, to use the features of v2.
174
210
 
175
- proxyForwardVersion = getFirstOrThrow(request.headers[MC_API_PROXY_HEADERS.FORWARD_TO_VERSION], "Missing \"X-MC-API-Forward-To-Version\" header.");
211
+ proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, "Missing \"X-MC-API-Forward-To-Version\" header.");
176
212
 
177
213
  if (proxyForwardVersion === 'v1') {
178
214
  // Fall back to legacy issuer domains
@@ -181,17 +217,17 @@ function createSessionAuthVerifier(options) {
181
217
 
182
218
  requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : (_request$originalUrl = request.originalUrl) !== null && _request$originalUrl !== void 0 ? _request$originalUrl : request.url;
183
219
 
184
- if (requestUrlPath) {
220
+ if (!(!requestUrlPath || !_startsWithInstanceProperty(requestUrlPath).call(requestUrlPath, '/'))) {
185
221
  _context3.next = 7;
186
222
  break;
187
223
  }
188
224
 
189
- throw new Error('Invalid request URI path. Please make sure that the `request` object has either a property `originalUrl` or `url`. If not, you should implement the `getRequestUrl` function. More info at https://docs.commercetools.com/custom-applications/concepts/integrate-with-your-own-api#validating-the-json-web-token');
225
+ throw new Error("Invalid request URI path \"".concat(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/custom-applications/concepts/integrate-with-your-own-api#validating-the-json-web-token"));
190
226
 
191
227
  case 7:
192
228
  audience = getConfiguredAudience(options, requestUrlPath);
193
229
  return _context3.abrupt("return", new _Promise(function (resolve, reject) {
194
- expressJwtMiddleware({
230
+ expressjwt({
195
231
  // Dynamically provide a signing key based on the kid in the header
196
232
  // and the singing keys provided by the JWKS endpoint
197
233
  secret: jwksRsa.expressJwtSecret(_objectSpread(_objectSpread({
@@ -3,16 +3,46 @@ import { CLOUD_IDENTIFIERS } from './constants';
3
3
  export declare type TAudience = string;
4
4
  export declare type TIssuer = string;
5
5
  export declare type TCloudIdentifier = typeof CLOUD_IDENTIFIERS[keyof typeof CLOUD_IDENTIFIERS];
6
+ export declare type TAudiencePolicy = 'forward-url-full-path' | 'forward-url-origin';
6
7
  export interface TBaseRequest {
7
8
  headers: Record<string, string | string[] | undefined>;
8
9
  url?: string;
9
10
  originalUrl?: string;
10
11
  }
11
12
  export declare 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
+ */
12
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
+ */
13
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
+ */
14
35
  inferIssuer?: boolean;
36
+ /**
37
+ * Options for the `jwksRsa.expressJwtSecret`
38
+ */
15
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
+ */
16
46
  getRequestUrl?: (request: Request) => string;
17
47
  };
18
48
  export declare type TSession = {
@@ -1,2 +1,3 @@
1
- declare const getFirstOrThrow: (value: string | string[] | undefined, errorMessage: string) => string;
2
- export { getFirstOrThrow };
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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commercetools-backend/express",
3
- "version": "21.3.4",
3
+ "version": "21.8.1",
4
4
  "description": "Zero-config HTTP server as Express.js to facilitate development",
5
5
  "bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
6
6
  "repository": {
@@ -20,12 +20,15 @@
20
20
  "dependencies": {
21
21
  "@babel/runtime": "^7.17.9",
22
22
  "@babel/runtime-corejs3": "^7.17.9",
23
- "@types/node": "16.11.26",
24
- "express": "4.17.3",
25
- "express-jwt": "6.1.1",
26
- "jwks-rsa": "2.0.5"
23
+ "@types/node": "16.11.33",
24
+ "express": "4.18.1",
25
+ "express-jwt": "7.7.0",
26
+ "jwks-rsa": "2.1.1"
27
27
  },
28
28
  "devDependencies": {
29
+ "@tsconfig/node16": "^1.0.2",
30
+ "@types/express-unless": "^0.5.3",
31
+ "@types/jsonwebtoken": "^8.5.8",
29
32
  "jose": "2.0.5",
30
33
  "msw": "0.39.2"
31
34
  }