@salesforce/pwa-kit-runtime 3.5.0-alpha.0 → 3.5.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/pwa-kit-runtime",
3
- "version": "3.5.0-alpha.0",
3
+ "version": "3.5.0-alpha.1",
4
4
  "description": "The PWAKit Runtime",
5
5
  "homepage": "https://github.com/SalesforceCommerceCloud/pwa-kit/tree/develop/packages/pwa-kit-runtime#readme",
6
6
  "bugs": {
@@ -37,7 +37,7 @@
37
37
  "cross-env": "^5.2.1",
38
38
  "express": "^4.19.2",
39
39
  "header-case": "1.0.1",
40
- "http-proxy-middleware": "0.21.0",
40
+ "http-proxy-middleware": "^2.0.6",
41
41
  "merge-descriptors": "^1.0.1",
42
42
  "morgan": "^1.10.0",
43
43
  "semver": "^7.5.2",
@@ -46,11 +46,11 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@loadable/component": "^5.15.3",
49
- "@salesforce/pwa-kit-dev": "3.5.0-alpha.0",
49
+ "@salesforce/pwa-kit-dev": "3.5.0-alpha.1",
50
50
  "@serverless/event-mocks": "^1.1.1",
51
51
  "aws-lambda-mock-context": "^3.2.1",
52
52
  "fs-extra": "^11.1.1",
53
- "internal-lib-build": "3.5.0-alpha.0",
53
+ "internal-lib-build": "3.5.0-alpha.1",
54
54
  "nock": "^13.3.0",
55
55
  "nodemon": "^2.0.22",
56
56
  "sinon": "^13.0.2",
@@ -58,7 +58,7 @@
58
58
  "supertest": "^4.0.2"
59
59
  },
60
60
  "peerDependencies": {
61
- "@salesforce/pwa-kit-dev": "3.5.0-alpha.0"
61
+ "@salesforce/pwa-kit-dev": "3.5.0-alpha.1"
62
62
  },
63
63
  "peerDependenciesMeta": {
64
64
  "@salesforce/pwa-kit-dev": {
@@ -72,5 +72,5 @@
72
72
  "publishConfig": {
73
73
  "directory": "dist"
74
74
  },
75
- "gitHead": "0edd6b2ea26b9c23502755b6ccc7316a538a4c7a"
75
+ "gitHead": "2df9f520e6de6585926743fb72bf8e77d515dcf1"
76
76
  }
@@ -21,9 +21,11 @@ var _express2 = require("./express");
21
21
  var _http = _interopRequireDefault(require("http"));
22
22
  var _https = _interopRequireDefault(require("https"));
23
23
  var _ssrShared = require("../../utils/ssr-shared");
24
+ var _configureProxy = require("../../utils/ssr-server/configure-proxy");
24
25
  var _awsServerlessExpress = _interopRequireDefault(require("aws-serverless-express"));
25
26
  var _morgan = _interopRequireDefault(require("morgan"));
26
27
  var _morganStream = require("../../utils/morgan-stream");
28
+ var _httpProxyMiddleware = require("http-proxy-middleware");
27
29
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
30
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
29
31
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -94,7 +96,14 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
94
96
  strictSSL: true,
95
97
  mobify: undefined,
96
98
  // Toggle cookies being passed and set
97
- localAllowCookies: false
99
+ localAllowCookies: false,
100
+ // Toggle for setting up the custom SLAS private client secret handler
101
+ useSLASPrivateClient: false,
102
+ // A regex for identifying which SLAS endpoints the custom SLAS private
103
+ // client secret handler will inject an Authorization header.
104
+ // Do not modify unless a project wants to customize additional SLAS
105
+ // endpoints that we currently do not support (ie. /oauth2/passwordless/token)
106
+ applySLASPrivateClientToEndpoints: /\/oauth2\/token/
98
107
  };
99
108
  options = _extends({}, defaults, options);
100
109
  (0, _ssrServer.setQuiet)(options.quiet || process.env.SSR_QUIET);
@@ -121,6 +130,10 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
121
130
  // Toggle cookies being passed and set. Can be overridden locally,
122
131
  // always uses MRT_ALLOW_COOKIES env remotely
123
132
  options.allowCookies = this._getAllowCookies(options);
133
+
134
+ // For test only – configure the SLAS private client secret proxy endpoint
135
+ options.slasHostName = this._getSlasEndpoint(options);
136
+ options.slasTarget = options.slasTarget || `https://${options.slasHostName}`;
124
137
  return options;
125
138
  },
126
139
  /**
@@ -157,6 +170,15 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
157
170
  _strictSSL(options) {
158
171
  return true;
159
172
  },
173
+ /**
174
+ * @private
175
+ */
176
+ _getSlasEndpoint(options) {
177
+ var _options$mobify, _options$mobify$app, _options$mobify$app$c, _options$mobify$app$c2;
178
+ if (!options.useSLASPrivateClient) return undefined;
179
+ const shortCode = (_options$mobify = options.mobify) === null || _options$mobify === void 0 ? void 0 : (_options$mobify$app = _options$mobify.app) === null || _options$mobify$app === void 0 ? void 0 : (_options$mobify$app$c = _options$mobify$app.commerceAPI) === null || _options$mobify$app$c === void 0 ? void 0 : (_options$mobify$app$c2 = _options$mobify$app$c.parameters) === null || _options$mobify$app$c2 === void 0 ? void 0 : _options$mobify$app$c2.shortCode;
180
+ return `${shortCode}.api.commercecloud.salesforce.com`;
181
+ },
160
182
  /**
161
183
  * @private
162
184
  */
@@ -245,6 +267,7 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
245
267
  this._setupMetricsFlushing(app);
246
268
  this._setupHealthcheck(app);
247
269
  this._setupProxying(app, options);
270
+ this._setupSlasPrivateClientProxy(app, options);
248
271
 
249
272
  // Beyond this point, we know that this is not a proxy request
250
273
  // and not a bundle request, so we can apply specific
@@ -508,6 +531,61 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
508
531
  });
509
532
  });
510
533
  },
534
+ /**
535
+ * @private
536
+ */
537
+ _handleMissingSlasPrivateEnvVar(app) {
538
+ app.use(_constants.SLAS_CUSTOM_PROXY_PATH, (_, res) => {
539
+ return res.status(501).json({
540
+ message: 'Environment variable PWA_KIT_SLAS_CLIENT_SECRET not set: Please set this environment variable to proceed.'
541
+ });
542
+ });
543
+ },
544
+ /**
545
+ * @private
546
+ */
547
+ _setupSlasPrivateClientProxy(app, options) {
548
+ var _options$mobify2, _options$mobify2$app, _options$mobify2$app$, _options$mobify2$app$2;
549
+ if (!options.useSLASPrivateClient) {
550
+ return;
551
+ }
552
+ (0, _ssrServer.localDevLog)(`Proxying ${_constants.SLAS_CUSTOM_PROXY_PATH} to ${options.slasTarget}`);
553
+ const clientId = (_options$mobify2 = options.mobify) === null || _options$mobify2 === void 0 ? void 0 : (_options$mobify2$app = _options$mobify2.app) === null || _options$mobify2$app === void 0 ? void 0 : (_options$mobify2$app$ = _options$mobify2$app.commerceAPI) === null || _options$mobify2$app$ === void 0 ? void 0 : (_options$mobify2$app$2 = _options$mobify2$app$.parameters) === null || _options$mobify2$app$2 === void 0 ? void 0 : _options$mobify2$app$2.clientId;
554
+ const clientSecret = process.env.PWA_KIT_SLAS_CLIENT_SECRET;
555
+ if (!clientSecret) {
556
+ this._handleMissingSlasPrivateEnvVar(app);
557
+ return;
558
+ }
559
+ const encodedSlasCredentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
560
+ app.use(_constants.SLAS_CUSTOM_PROXY_PATH, (0, _httpProxyMiddleware.createProxyMiddleware)({
561
+ target: options.slasTarget,
562
+ changeOrigin: true,
563
+ pathRewrite: {
564
+ [_constants.SLAS_CUSTOM_PROXY_PATH]: ''
565
+ },
566
+ onProxyReq: (proxyRequest, incomingRequest) => {
567
+ var _incomingRequest$path;
568
+ (0, _configureProxy.applyProxyRequestHeaders)({
569
+ proxyRequest,
570
+ incomingRequest,
571
+ proxyPath: _constants.SLAS_CUSTOM_PROXY_PATH,
572
+ targetHost: options.slasHostName,
573
+ targetProtocol: 'https'
574
+ });
575
+
576
+ // We pattern match and add client secrets only to endpoints that
577
+ // match the regex specified by options.applySLASPrivateClientToEndpoints.
578
+ // By default, this regex matches only calls to SLAS /oauth2/token
579
+ // (see option defaults at the top of this file).
580
+ // Other SLAS endpoints, ie. SLAS authenticate (/oauth2/login) and
581
+ // SLAS logout (/oauth2/logout), use the Authorization header for a different
582
+ // purpose so we don't want to overwrite the header for those calls.
583
+ if ((_incomingRequest$path = incomingRequest.path) !== null && _incomingRequest$path !== void 0 && _incomingRequest$path.match(options.applySLASPrivateClientToEndpoints)) {
584
+ proxyRequest.setHeader('Authorization', `Basic ${encodedSlasCredentials}`);
585
+ }
586
+ }
587
+ }));
588
+ },
511
589
  /**
512
590
  * @private
513
591
  */
@@ -21,4 +21,16 @@ describe('the once function', () => {
21
21
  expect(fn.mock.calls).toHaveLength(1);
22
22
  expect(v1).toBe(v2); // The exact same instance
23
23
  });
24
+ });
25
+ describe('remote server factory test coverage', () => {
26
+ test('getSlasEndpoint returns undefined if useSLASPrivateClient is false', () => {
27
+ const endpoint = _buildRemoteServer.RemoteServerFactory._getSlasEndpoint({});
28
+ expect(endpoint).toBeUndefined();
29
+ });
30
+ test('getSlasEndpoint returns endpoint if useSLASPrivateClient is true', () => {
31
+ const endpoint = _buildRemoteServer.RemoteServerFactory._getSlasEndpoint({
32
+ useSLASPrivateClient: true
33
+ });
34
+ expect(endpoint).toBeDefined();
35
+ });
24
36
  });
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.X_ORIGINAL_CONTENT_TYPE = exports.X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_FROM_CACHE = exports.STRICT_TRANSPORT_SECURITY = exports.STATIC_ASSETS = exports.SET_COOKIE = exports.PROXY_PATH_PREFIX = exports.NO_CACHE = exports.CONTENT_TYPE = exports.CONTENT_SECURITY_POLICY = exports.CONTENT_ENCODING = exports.CACHE_CONTROL = exports.BUILD = exports.APPLICATION_OCTET_STREAM = void 0;
6
+ exports.X_ORIGINAL_CONTENT_TYPE = exports.X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_FROM_CACHE = exports.STRICT_TRANSPORT_SECURITY = exports.STATIC_ASSETS = exports.SLAS_CUSTOM_PROXY_PATH = exports.SET_COOKIE = exports.PROXY_PATH_PREFIX = exports.NO_CACHE = exports.CONTENT_TYPE = exports.CONTENT_SECURITY_POLICY = exports.CONTENT_ENCODING = exports.CACHE_CONTROL = exports.BUILD = exports.APPLICATION_OCTET_STREAM = void 0;
7
7
  /*
8
8
  * Copyright (c) 2021, salesforce.com, inc.
9
9
  * All rights reserved.
@@ -24,4 +24,5 @@ const SET_COOKIE = exports.SET_COOKIE = 'set-cookie';
24
24
  const CACHE_CONTROL = exports.CACHE_CONTROL = 'cache-control';
25
25
  const NO_CACHE = exports.NO_CACHE = 'max-age=0, nocache, nostore, must-revalidate';
26
26
  const CONTENT_SECURITY_POLICY = exports.CONTENT_SECURITY_POLICY = 'content-security-policy';
27
- const STRICT_TRANSPORT_SECURITY = exports.STRICT_TRANSPORT_SECURITY = 'strict-transport-security';
27
+ const STRICT_TRANSPORT_SECURITY = exports.STRICT_TRANSPORT_SECURITY = 'strict-transport-security';
28
+ const SLAS_CUSTOM_PROXY_PATH = exports.SLAS_CUSTOM_PROXY_PATH = '/mobify/scapi/shopper/auth';
@@ -8,16 +8,19 @@ var _path = _interopRequireDefault(require("path"));
8
8
  var _sinon = _interopRequireDefault(require("sinon"));
9
9
  var _superagent = _interopRequireDefault(require("superagent"));
10
10
  var _supertest = _interopRequireDefault(require("supertest"));
11
+ var _express = _interopRequireDefault(require("express"));
11
12
  var _ssrCache = require("../../utils/ssr-cache");
12
13
  var _ssrServer = require("../../utils/ssr-server");
13
14
  var ssrServerUtils = _interopRequireWildcard(require("../../utils/ssr-server/utils"));
14
15
  var _buildRemoteServer = require("./build-remote-server");
15
16
  var _constants = require("./constants");
16
- var _express = require("./express");
17
+ var _express2 = require("./express");
17
18
  var _crypto = require("crypto");
18
19
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
19
20
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
20
21
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
23
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
21
24
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
22
25
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
23
26
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -78,7 +81,8 @@ const opts = (overrides = {}) => {
78
81
  https: httpsAgent
79
82
  },
80
83
  defaultCacheTimeSeconds: 123,
81
- enableLegacyRemoteProxying: false
84
+ enableLegacyRemoteProxying: false,
85
+ useSLASPrivateClient: false
82
86
  };
83
87
  return _objectSpread(_objectSpread({}, defaults), overrides);
84
88
  };
@@ -203,11 +207,11 @@ describe('SSRServer operation', () => {
203
207
  app._requestMonitor._responseFinished(response1);
204
208
  expect(app._requestMonitor._pendingResponses.ids).toEqual([]);
205
209
  const promise1 = app._requestMonitor._waitForResponses();
206
- expect(promise1).toBe(_express.RESOLVED_PROMISE);
210
+ expect(promise1).toBe(_express2.RESOLVED_PROMISE);
207
211
  app._requestMonitor._responseStarted(response1);
208
212
  expect(app._requestMonitor._pendingResponses.ids).toEqual([1]);
209
213
  const promise2 = app._requestMonitor._waitForResponses();
210
- expect(promise2).not.toBe(_express.RESOLVED_PROMISE);
214
+ expect(promise2).not.toBe(_express2.RESOLVED_PROMISE);
211
215
  app._requestMonitor._responseStarted(response2);
212
216
  expect(app._requestMonitor._pendingResponses.ids).toEqual([1, 2]);
213
217
  const promise3 = app._requestMonitor._waitForResponses();
@@ -389,7 +393,7 @@ describe('SSRServer operation', () => {
389
393
  test('should support redirects to bundle assets', () => {
390
394
  const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
391
395
  const route = (req, res) => {
392
- (0, _express.respondFromBundle)({
396
+ (0, _express2.respondFromBundle)({
393
397
  req,
394
398
  res
395
399
  });
@@ -517,12 +521,12 @@ describe('SSRServer persistent caching', () => {
517
521
  key: keyFromURL(req.url)
518
522
  };
519
523
  const shouldCacheResponse = (req, res) => res.statusCode >= 200 && res.statusCode < 300;
520
- return Promise.resolve().then(() => (0, _express.getResponseFromCache)(cacheArgs)).then(entry => {
524
+ return Promise.resolve().then(() => (0, _express2.getResponseFromCache)(cacheArgs)).then(entry => {
521
525
  if (entry.found) {
522
- (0, _express.sendCachedResponse)(entry);
526
+ (0, _express2.sendCachedResponse)(entry);
523
527
  } else {
524
528
  if (shouldCache) {
525
- (0, _express.cacheResponseWhenDone)(_objectSpread({
529
+ (0, _express2.cacheResponseWhenDone)(_objectSpread({
526
530
  shouldCacheResponse
527
531
  }, cacheArgs));
528
532
  }
@@ -727,7 +731,7 @@ describe('SSRServer persistent caching', () => {
727
731
  });
728
732
  });
729
733
  test('Try to send non-cached response', () => {
730
- expect(() => (0, _express.sendCachedResponse)(new _ssrServer.CachedResponse({}))).toThrow('non-cached');
734
+ expect(() => (0, _express2.sendCachedResponse)(new _ssrServer.CachedResponse({}))).toThrow('non-cached');
731
735
  });
732
736
  });
733
737
  describe('generateCacheKey', () => {
@@ -742,41 +746,41 @@ describe('generateCacheKey', () => {
742
746
  }, overrides);
743
747
  };
744
748
  test('returns expected results', () => {
745
- expect((0, _express.generateCacheKey)(mockRequest({
749
+ expect((0, _express2.generateCacheKey)(mockRequest({
746
750
  url: '/test/1?id=abc'
747
751
  })).indexOf('/test/1')).toBe(0);
748
752
  });
749
753
  test('path affects key', () => {
750
- const result1 = (0, _express.generateCacheKey)(mockRequest({
754
+ const result1 = (0, _express2.generateCacheKey)(mockRequest({
751
755
  url: '/test2a/'
752
756
  }));
753
- expect((0, _express.generateCacheKey)(mockRequest({
757
+ expect((0, _express2.generateCacheKey)(mockRequest({
754
758
  url: '/testab/'
755
759
  }))).not.toEqual(result1);
756
760
  });
757
761
  test('query affects key', () => {
758
- const result1 = (0, _express.generateCacheKey)(mockRequest({
762
+ const result1 = (0, _express2.generateCacheKey)(mockRequest({
759
763
  url: '/test3?a=1'
760
764
  }));
761
- expect((0, _express.generateCacheKey)(mockRequest({
765
+ expect((0, _express2.generateCacheKey)(mockRequest({
762
766
  url: '/test3?a=2'
763
767
  }))).not.toEqual(result1);
764
768
  });
765
769
  test('request class affects key', () => {
766
- const result1 = (0, _express.generateCacheKey)(mockRequest());
770
+ const result1 = (0, _express2.generateCacheKey)(mockRequest());
767
771
  const request2 = mockRequest({
768
772
  headers: {
769
773
  'x-mobify-request-class': 'bot'
770
774
  }
771
775
  });
772
- expect((0, _express.generateCacheKey)(request2)).not.toEqual(result1);
773
- expect((0, _express.generateCacheKey)(request2, {
776
+ expect((0, _express2.generateCacheKey)(request2)).not.toEqual(result1);
777
+ expect((0, _express2.generateCacheKey)(request2, {
774
778
  ignoreRequestClass: true
775
779
  })).toEqual(result1);
776
780
  });
777
781
  test('extras affect key', () => {
778
- const result1 = (0, _express.generateCacheKey)(mockRequest());
779
- expect((0, _express.generateCacheKey)(mockRequest(), {
782
+ const result1 = (0, _express2.generateCacheKey)(mockRequest());
783
+ expect((0, _express2.generateCacheKey)(mockRequest(), {
780
784
  extras: ['123']
781
785
  })).not.toEqual(result1);
782
786
  });
@@ -836,10 +840,10 @@ describe('getRuntime', () => {
836
840
  expectedRuntime
837
841
  }) => {
838
842
  process.env = _objectSpread(_objectSpread({}, process.env), env);
839
- expect((0, _express.getRuntime)()).toMatchObject(matchExceptFunctionValues(expectedRuntime));
843
+ expect((0, _express2.getRuntime)()).toMatchObject(matchExceptFunctionValues(expectedRuntime));
840
844
  });
841
845
  test('should return a remote/development runtime bound to the correct context', () => {
842
- const mockDevRuntime = (0, _express.getRuntime)();
846
+ const mockDevRuntime = (0, _express2.getRuntime)();
843
847
  const func = mockDevRuntime.returnMyName;
844
848
  expect(func()).toBe(MockDevServerFactory.name);
845
849
  });
@@ -873,4 +877,81 @@ describe('DevServer middleware', () => {
873
877
  }));
874
878
  expect(warn.mock.calls).toEqual([['The SSR Server has _strictSSL turned off for https requests']]);
875
879
  });
880
+ });
881
+ describe('SLAS private client proxy', () => {
882
+ const savedEnvironment = _extends({}, process.env);
883
+ let proxyApp;
884
+ const proxyPort = 12345;
885
+ const proxyPath = '/responseHeaders';
886
+ const slasTarget = `http://localhost:${proxyPort}${proxyPath}`;
887
+ beforeAll(() => {
888
+ // by setting slasTarget, rather than forwarding the request to SLAS,
889
+ // we send the proxy request here so we can return the request headers
890
+ proxyApp = (0, _express.default)();
891
+ proxyApp.use(proxyPath, (req, res) => {
892
+ res.send(req.headers);
893
+ });
894
+ proxyApp.listen(proxyPort);
895
+ });
896
+ afterEach(() => {
897
+ process.env = savedEnvironment;
898
+ });
899
+ afterAll(() => {
900
+ proxyApp.close();
901
+ });
902
+ test('should not create proxy by default', () => {
903
+ const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
904
+ return (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth').expect(404);
905
+ });
906
+ test('should return HTTP 501 if PWA_KIT_SLAS_CLIENT_SECRET env var not set', () => {
907
+ const app = _buildRemoteServer.RemoteServerFactory._createApp(opts({
908
+ useSLASPrivateClient: true
909
+ }));
910
+ return (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth').expect(501);
911
+ });
912
+ test('does not insert client secret if request not for /oauth2/token', /*#__PURE__*/_asyncToGenerator(function* () {
913
+ process.env.PWA_KIT_SLAS_CLIENT_SECRET = 'a secret';
914
+ const app = _buildRemoteServer.RemoteServerFactory._createApp(opts({
915
+ mobify: {
916
+ app: {
917
+ commerceAPI: {
918
+ parameters: {
919
+ clientId: 'clientId',
920
+ shortCode: 'shortCode'
921
+ }
922
+ }
923
+ }
924
+ },
925
+ useSLASPrivateClient: true,
926
+ slasTarget: slasTarget
927
+ }));
928
+ return yield (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth/somePath').then(response => {
929
+ expect(response.body.authorization).toBeUndefined();
930
+ expect(response.body.host).toBe('shortCode.api.commercecloud.salesforce.com');
931
+ expect(response.body['x-mobify']).toBe('true');
932
+ });
933
+ }), 15000);
934
+ test('inserts client secret if request is for /oauth2/token', /*#__PURE__*/_asyncToGenerator(function* () {
935
+ process.env.PWA_KIT_SLAS_CLIENT_SECRET = 'a secret';
936
+ const encodedCredentials = Buffer.from('clientId:a secret').toString('base64');
937
+ const app = _buildRemoteServer.RemoteServerFactory._createApp(opts({
938
+ mobify: {
939
+ app: {
940
+ commerceAPI: {
941
+ parameters: {
942
+ clientId: 'clientId',
943
+ shortCode: 'shortCode'
944
+ }
945
+ }
946
+ }
947
+ },
948
+ useSLASPrivateClient: true,
949
+ slasTarget: slasTarget
950
+ }));
951
+ return yield (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth/oauth2/token').then(response => {
952
+ expect(response.body.authorization).toBe(`Basic ${encodedCredentials}`);
953
+ expect(response.body.host).toBe('shortCode.api.commercecloud.salesforce.com');
954
+ expect(response.body['x-mobify']).toBe('true');
955
+ });
956
+ }), 15000);
876
957
  });
@@ -3,13 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.configureProxyConfigs = exports.configureProxy = exports.ALLOWED_CACHING_PROXY_REQUEST_METHODS = void 0;
7
- var _httpProxyMiddleware = _interopRequireDefault(require("http-proxy-middleware"));
6
+ exports.configureProxyConfigs = exports.configureProxy = exports.applyProxyRequestHeaders = exports.ALLOWED_CACHING_PROXY_REQUEST_METHODS = void 0;
7
+ var _httpProxyMiddleware = require("http-proxy-middleware");
8
8
  var _ssrProxying = require("../ssr-proxying");
9
9
  var _ssrShared = require("../ssr-shared");
10
10
  var _processExpressResponse = require("./process-express-response");
11
11
  var _utils = require("./utils");
12
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
12
  /*
14
13
  * Copyright (c) 2022, Salesforce, Inc.
15
14
  * All rights reserved.
@@ -29,6 +28,73 @@ const ALLOWED_CACHING_PROXY_REQUEST_METHODS = exports.ALLOWED_CACHING_PROXY_REQU
29
28
  */
30
29
  const generalProxyPathRE = /^\/mobify\/proxy\/([^/]+)(\/.*)$/;
31
30
 
31
+ /**
32
+ * Apply proxy headers to a request that is being proxied.
33
+ *
34
+ * This function is intended to be called from within a proxy's
35
+ * onProxyReq method.
36
+ *
37
+ * For more details on the headers being applied,
38
+ * see ssr-proxying.js rewriteProxyRequestHeaders method
39
+ * @private
40
+ * @function
41
+ * @param proxyRequest {http.ClientRequest} the request that will be
42
+ * sent to the target host
43
+ * @param incomingRequest {http.IncomingMessage} the request made to
44
+ * this Express app that prompted the proxying
45
+ * @param caching {Boolean} true for a caching proxy, false for a standard proxy
46
+ * @param logging {Boolean} true to log operations
47
+ * @param proxyPath {String} the path being proxied (e.g. /mobify/proxy/base/
48
+ * or /mobify/caching/base/)
49
+ * @param targetHost {String} the target hostname (host+port)
50
+ * @param targetProtocol {String} the protocol to use to make requests to
51
+ * the target ('http' or 'https')
52
+ */
53
+ const applyProxyRequestHeaders = ({
54
+ proxyRequest,
55
+ incomingRequest,
56
+ caching = false,
57
+ logging = !(0, _utils.isRemote)() && _utils.verboseProxyLogging,
58
+ proxyPath,
59
+ targetHost,
60
+ targetProtocol
61
+ }) => {
62
+ const url = incomingRequest.url;
63
+ const headers = incomingRequest.headers;
64
+ /* istanbul ignore next */
65
+ if (logging) {
66
+ console.log(`Proxy: request for ${proxyPath}${url} => ${targetProtocol}://${targetHost}/${url}`);
67
+ }
68
+ const newHeaders = (0, _ssrProxying.rewriteProxyRequestHeaders)({
69
+ caching,
70
+ headers,
71
+ headerFormat: 'http',
72
+ logging,
73
+ proxyPath,
74
+ targetHost,
75
+ targetProtocol
76
+ });
77
+
78
+ // Copy any new and updated headers to the proxyRequest
79
+ // using setHeader.
80
+ Object.entries(newHeaders).forEach(
81
+ // setHeader always replaces any current value.
82
+ ([key, value]) => proxyRequest.setHeader(key, value));
83
+
84
+ // Handle deletion of headers.
85
+ // Iterate over the keys of incomingRequest.headers - for every
86
+ // key, if the value is not present in newHeaders, we remove
87
+ // that value from proxyRequest's headers.
88
+ Object.keys(headers).forEach(key => {
89
+ // We delete the header on any falsy value, since
90
+ // there's no use case where we supply an empty header
91
+ // value.
92
+ if (!newHeaders[key]) {
93
+ proxyRequest.removeHeader(key);
94
+ }
95
+ });
96
+ };
97
+
32
98
  /**
33
99
  * Configure proxying for a path.
34
100
  * @private
@@ -46,6 +112,7 @@ const generalProxyPathRE = /^\/mobify\/proxy\/([^/]+)(\/.*)$/;
46
112
  * standard proxy.
47
113
  * @returns {middleware} function to pass to expressApp.use()
48
114
  */
115
+ exports.applyProxyRequestHeaders = applyProxyRequestHeaders;
49
116
  const configureProxy = ({
50
117
  appHostname,
51
118
  proxyPath,
@@ -105,41 +172,14 @@ const configureProxy = ({
105
172
  * this Express app that prompted the proxying
106
173
  */
107
174
  onProxyReq: (proxyRequest, incomingRequest) => {
108
- const url = incomingRequest.url;
109
- /* istanbul ignore next */
110
- if (!(0, _utils.isRemote)() && _utils.verboseProxyLogging) {
111
- console.log(`Proxy: request for ${proxyPath}${url} => ${targetOrigin}/${url}`);
112
- }
113
-
114
- // Rewrite key headers.
115
- const newHeaders = (0, _ssrProxying.rewriteProxyRequestHeaders)({
175
+ applyProxyRequestHeaders({
176
+ proxyRequest,
177
+ incomingRequest,
116
178
  caching,
117
- headers: incomingRequest.headers,
118
- headerFormat: 'http',
119
- logging: !(0, _utils.isRemote)() && _utils.verboseProxyLogging,
120
179
  proxyPath,
121
180
  targetHost,
122
181
  targetProtocol
123
182
  });
124
-
125
- // Copy any new and updated headers to the proxyRequest
126
- // using setHeader.
127
- Object.entries(newHeaders).forEach(
128
- // setHeader always replaces any current value.
129
- ([key, value]) => proxyRequest.setHeader(key, value));
130
-
131
- // Handle deletion of headers.
132
- // Iterate over the keys of incomingRequest.headers - for every
133
- // key, if the value is not present in newHeaders, we remove
134
- // that value from proxyRequest's headers.
135
- Object.keys(incomingRequest.headers).forEach(key => {
136
- // We delete the header on any falsy value, since
137
- // there's no use case where we supply an empty header
138
- // value.
139
- if (!newHeaders[key]) {
140
- proxyRequest.removeHeader(key);
141
- }
142
- });
143
183
  },
144
184
  onProxyRes: (proxyResponse, req) => {
145
185
  /* istanbul ignore next */
@@ -183,7 +223,7 @@ const configureProxy = ({
183
223
  // The origin (protocol + host) to which we proxy
184
224
  target: targetOrigin
185
225
  };
186
- const proxyFunc = (0, _httpProxyMiddleware.default)(config);
226
+ const proxyFunc = (0, _httpProxyMiddleware.createProxyMiddleware)(config);
187
227
 
188
228
  // For a standard proxy, we're done
189
229
  if (!caching) {