@commercetools-backend/express 22.2.1 → 22.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -62,42 +62,32 @@ const MC_API_PROXY_HEADERS = {
|
|
|
62
62
|
|
|
63
63
|
const getHeaderByCaseInsensitiveKey = (headers, headerKey) => {
|
|
64
64
|
var _context;
|
|
65
|
-
|
|
66
65
|
const matchingHeader = _findInstanceProperty__default["default"](_context = _Object$entries__default["default"](headers)).call(_context, _ref => {
|
|
67
66
|
let _ref2 = _slicedToArray(_ref, 1),
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
key = _ref2[0];
|
|
70
68
|
return headerKey.toLowerCase() === key.toLowerCase();
|
|
71
69
|
});
|
|
72
|
-
|
|
73
70
|
if (matchingHeader && matchingHeader.length > 0) {
|
|
74
71
|
const _matchingHeader = _slicedToArray(matchingHeader, 2),
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
headerValue = _matchingHeader[1];
|
|
77
73
|
return _Array$isArray__default["default"](headerValue) ? headerValue[0] : headerValue;
|
|
78
74
|
}
|
|
79
|
-
|
|
80
75
|
return undefined;
|
|
81
76
|
};
|
|
82
|
-
|
|
83
77
|
const getFirstHeaderValueOrThrow = (headers, headerKey, errorMessage) => {
|
|
84
78
|
const headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
|
|
85
|
-
|
|
86
79
|
if (!headerValue) {
|
|
87
80
|
throw new Error(errorMessage);
|
|
88
81
|
}
|
|
89
|
-
|
|
90
82
|
return headerValue;
|
|
91
83
|
};
|
|
92
84
|
|
|
93
85
|
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; }
|
|
94
|
-
|
|
95
86
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var _context3, _context4; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty__default["default"](_context3 = ownKeys(Object(source), !0)).call(_context3, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](target, _Object$getOwnPropertyDescriptors__default["default"](source)) : _forEachInstanceProperty__default["default"](_context4 = ownKeys(Object(source))).call(_context4, function (key) { _Object$defineProperty__default["default"](target, key, _Object$getOwnPropertyDescriptor__default["default"](source, key)); }); } return target; }
|
|
96
|
-
const decodedTokenKey = 'decoded_token';
|
|
97
|
-
|
|
87
|
+
const decodedTokenKey = 'decoded_token';
|
|
88
|
+
// Assign a session object to the request object.
|
|
98
89
|
const writeSessionContext = request => {
|
|
99
90
|
const decodedToken = request[decodedTokenKey];
|
|
100
|
-
|
|
101
91
|
if (decodedToken) {
|
|
102
92
|
const publicClaimForProjectKey = "".concat(decodedToken.iss, "/claims/project_key");
|
|
103
93
|
const publicClaimForUserPermissionsKey = "".concat(decodedToken.iss, "/claims/user_permissions");
|
|
@@ -106,143 +96,118 @@ const writeSessionContext = request => {
|
|
|
106
96
|
projectKey: decodedToken[publicClaimForProjectKey]
|
|
107
97
|
};
|
|
108
98
|
const userPermissions = decodedToken[publicClaimForUserPermissionsKey];
|
|
109
|
-
|
|
110
99
|
if (Boolean(userPermissions === null || userPermissions === void 0 ? void 0 : userPermissions.length)) {
|
|
111
100
|
request.session.userPermissions = userPermissions;
|
|
112
101
|
}
|
|
113
|
-
}
|
|
114
|
-
|
|
102
|
+
}
|
|
115
103
|
|
|
104
|
+
// Remove the field used by the JWT middleware.
|
|
116
105
|
delete request.decoded_token;
|
|
117
|
-
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Given a cloud identifier, try to map it to one of the supported
|
|
118
109
|
// environments and return the MC API URL for that environment.
|
|
119
110
|
// The URL points to the new hostnames.
|
|
120
111
|
// https://docs.commercetools.com/custom-applications/concepts/merchant-center-api#hostnames
|
|
121
|
-
|
|
122
|
-
|
|
123
112
|
const mapCloudIdentifierToIssuer = issuer => {
|
|
124
113
|
switch (issuer) {
|
|
125
114
|
case CLOUD_IDENTIFIERS.GCP_AU:
|
|
126
115
|
return MC_API_URLS.GCP_AU;
|
|
127
|
-
|
|
128
116
|
case CLOUD_IDENTIFIERS.GCP_EU:
|
|
129
117
|
return MC_API_URLS.GCP_EU;
|
|
130
|
-
|
|
131
118
|
case CLOUD_IDENTIFIERS.GCP_US:
|
|
132
119
|
return MC_API_URLS.GCP_US;
|
|
133
|
-
|
|
134
120
|
case CLOUD_IDENTIFIERS.AWS_FRA:
|
|
135
121
|
return MC_API_URLS.AWS_FRA;
|
|
136
|
-
|
|
137
122
|
case CLOUD_IDENTIFIERS.AWS_OHIO:
|
|
138
123
|
return MC_API_URLS.AWS_OHIO;
|
|
139
|
-
|
|
140
124
|
default:
|
|
141
125
|
return undefined;
|
|
142
126
|
}
|
|
143
|
-
};
|
|
127
|
+
};
|
|
128
|
+
// Given a cloud identifier, try to map it to a legacy hostname.
|
|
144
129
|
// This is for backwards compatibility.
|
|
145
|
-
|
|
146
|
-
|
|
147
130
|
const mapToLegacyIssuer = cloudIdentifier => {
|
|
148
131
|
switch (cloudIdentifier) {
|
|
149
132
|
case CLOUD_IDENTIFIERS.GCP_EU:
|
|
150
133
|
return 'https://mc-api.commercetools.com';
|
|
151
|
-
|
|
152
134
|
case CLOUD_IDENTIFIERS.GCP_US:
|
|
153
135
|
return 'https://mc-api.commercetools.co';
|
|
154
|
-
|
|
155
136
|
default:
|
|
156
137
|
return undefined;
|
|
157
138
|
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
|
|
139
|
+
};
|
|
140
|
+
// Verifies that the issuer is a valid URL.
|
|
161
141
|
const throwIfIssuerIsNotAValidUrl = issuer => {
|
|
162
142
|
try {
|
|
163
143
|
new _URL__default["default"](issuer);
|
|
164
144
|
} catch (error) {
|
|
165
145
|
throw new Error("Invalid issuer URL \"".concat(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/custom-applications/concepts/merchant-center-api#hostnames."));
|
|
166
146
|
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
|
|
147
|
+
};
|
|
148
|
+
// Validates required option values.
|
|
170
149
|
const validateRequiredValues = options => {
|
|
171
150
|
if (!options.audience) {
|
|
172
151
|
throw new Error("Missing required option \"audience\"");
|
|
173
152
|
}
|
|
174
|
-
|
|
175
153
|
if (!options.issuer) {
|
|
176
154
|
throw new Error("Missing required option \"issuer\"");
|
|
177
155
|
}
|
|
178
|
-
};
|
|
156
|
+
};
|
|
157
|
+
// Attempt to parse the given issuer. If the value is a cloud identifier, it will
|
|
179
158
|
// be mapped to one of the supported values. If not, we assume the value is a valid URL.
|
|
180
|
-
|
|
181
|
-
|
|
182
159
|
const getConfiguredDefaultIssuer = options => {
|
|
183
160
|
const issuer = mapCloudIdentifierToIssuer(options.issuer);
|
|
184
|
-
|
|
185
161
|
if (!issuer) {
|
|
186
162
|
throwIfIssuerIsNotAValidUrl(options.issuer);
|
|
187
163
|
return options.issuer;
|
|
188
164
|
}
|
|
189
|
-
|
|
190
165
|
return issuer;
|
|
191
|
-
};
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Construct the audience from the given option + the request path.
|
|
192
169
|
// If the request path is `/`, do not append it to the audience, otherwise
|
|
193
170
|
// the token validation might fail because of mismatching audiences.
|
|
194
|
-
|
|
195
|
-
|
|
196
171
|
const getConfiguredAudience = (options, requestPath) => {
|
|
197
172
|
var _context;
|
|
198
|
-
|
|
199
173
|
// remove the trailing slash
|
|
200
174
|
const url = new _URL__default["default"](_concatInstanceProperty__default["default"](_context = "".concat(options.audience.replace(/\/?$/, ''))).call(_context, requestPath));
|
|
201
|
-
|
|
202
175
|
switch (options.audiencePolicy) {
|
|
203
176
|
case 'forward-url-origin':
|
|
204
177
|
return url.origin;
|
|
205
|
-
|
|
206
178
|
default:
|
|
207
179
|
{
|
|
208
180
|
var _context2;
|
|
209
|
-
|
|
210
181
|
if (requestPath === '/') {
|
|
211
182
|
return url.origin;
|
|
212
183
|
}
|
|
213
|
-
|
|
214
184
|
return _concatInstanceProperty__default["default"](_context2 = "".concat(url.origin)).call(_context2, url.pathname);
|
|
215
185
|
}
|
|
216
186
|
}
|
|
217
187
|
};
|
|
218
|
-
|
|
219
188
|
function createSessionAuthVerifier(options) {
|
|
220
189
|
validateRequiredValues(options);
|
|
221
|
-
const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
|
|
190
|
+
const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
|
|
222
191
|
|
|
192
|
+
// Returns an async HTTP handler.
|
|
223
193
|
return async (request, response) => {
|
|
224
194
|
var _mapCloudIdentifierTo, _request$originalUrl;
|
|
225
|
-
|
|
226
195
|
// Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
|
|
227
196
|
const cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, "Missing \"X-MC-API-Cloud-Identifier\" header.");
|
|
228
|
-
let issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer;
|
|
229
|
-
// The version should be sent by the client making the request, to use the features of v2.
|
|
197
|
+
let issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer;
|
|
230
198
|
|
|
199
|
+
// Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
|
|
200
|
+
// The version should be sent by the client making the request, to use the features of v2.
|
|
231
201
|
const proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, "Missing \"X-MC-API-Forward-To-Version\" header.");
|
|
232
|
-
|
|
233
202
|
if (proxyForwardVersion === 'v1') {
|
|
234
203
|
var _mapToLegacyIssuer;
|
|
235
|
-
|
|
236
204
|
// Fall back to legacy issuer domains
|
|
237
205
|
issuer = (_mapToLegacyIssuer = mapToLegacyIssuer(cloudIdentifierHeader)) !== null && _mapToLegacyIssuer !== void 0 ? _mapToLegacyIssuer : issuer;
|
|
238
206
|
}
|
|
239
|
-
|
|
240
207
|
const requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : (_request$originalUrl = request.originalUrl) !== null && _request$originalUrl !== void 0 ? _request$originalUrl : request.url;
|
|
241
|
-
|
|
242
208
|
if (!requestUrlPath || !_startsWithInstanceProperty__default["default"](requestUrlPath).call(requestUrlPath, '/')) {
|
|
243
209
|
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"));
|
|
244
210
|
}
|
|
245
|
-
|
|
246
211
|
const audience = getConfiguredAudience(options, requestUrlPath);
|
|
247
212
|
return new _Promise__default["default"]((resolve, reject) => {
|
|
248
213
|
expressJwt.expressjwt({
|
|
@@ -261,8 +226,8 @@ function createSessionAuthVerifier(options) {
|
|
|
261
226
|
// Validate the audience and the issuer.
|
|
262
227
|
audience,
|
|
263
228
|
issuer,
|
|
264
|
-
algorithms: ['RS256']
|
|
265
|
-
|
|
229
|
+
algorithms: ['RS256']
|
|
230
|
+
// @ts-ignore: the middleware expects an Express.js Request/Response objects
|
|
266
231
|
})(request, response !== null && response !== void 0 ? response : {}, error => {
|
|
267
232
|
if (error) {
|
|
268
233
|
reject(error);
|
|
@@ -62,42 +62,32 @@ const MC_API_PROXY_HEADERS = {
|
|
|
62
62
|
|
|
63
63
|
const getHeaderByCaseInsensitiveKey = (headers, headerKey) => {
|
|
64
64
|
var _context;
|
|
65
|
-
|
|
66
65
|
const matchingHeader = _findInstanceProperty__default["default"](_context = _Object$entries__default["default"](headers)).call(_context, _ref => {
|
|
67
66
|
let _ref2 = _slicedToArray(_ref, 1),
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
key = _ref2[0];
|
|
70
68
|
return headerKey.toLowerCase() === key.toLowerCase();
|
|
71
69
|
});
|
|
72
|
-
|
|
73
70
|
if (matchingHeader && matchingHeader.length > 0) {
|
|
74
71
|
const _matchingHeader = _slicedToArray(matchingHeader, 2),
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
headerValue = _matchingHeader[1];
|
|
77
73
|
return _Array$isArray__default["default"](headerValue) ? headerValue[0] : headerValue;
|
|
78
74
|
}
|
|
79
|
-
|
|
80
75
|
return undefined;
|
|
81
76
|
};
|
|
82
|
-
|
|
83
77
|
const getFirstHeaderValueOrThrow = (headers, headerKey, errorMessage) => {
|
|
84
78
|
const headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
|
|
85
|
-
|
|
86
79
|
if (!headerValue) {
|
|
87
80
|
throw new Error(errorMessage);
|
|
88
81
|
}
|
|
89
|
-
|
|
90
82
|
return headerValue;
|
|
91
83
|
};
|
|
92
84
|
|
|
93
85
|
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; }
|
|
94
|
-
|
|
95
86
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var _context3, _context4; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty__default["default"](_context3 = ownKeys(Object(source), !0)).call(_context3, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](target, _Object$getOwnPropertyDescriptors__default["default"](source)) : _forEachInstanceProperty__default["default"](_context4 = ownKeys(Object(source))).call(_context4, function (key) { _Object$defineProperty__default["default"](target, key, _Object$getOwnPropertyDescriptor__default["default"](source, key)); }); } return target; }
|
|
96
|
-
const decodedTokenKey = 'decoded_token';
|
|
97
|
-
|
|
87
|
+
const decodedTokenKey = 'decoded_token';
|
|
88
|
+
// Assign a session object to the request object.
|
|
98
89
|
const writeSessionContext = request => {
|
|
99
90
|
const decodedToken = request[decodedTokenKey];
|
|
100
|
-
|
|
101
91
|
if (decodedToken) {
|
|
102
92
|
const publicClaimForProjectKey = "".concat(decodedToken.iss, "/claims/project_key");
|
|
103
93
|
const publicClaimForUserPermissionsKey = "".concat(decodedToken.iss, "/claims/user_permissions");
|
|
@@ -106,143 +96,118 @@ const writeSessionContext = request => {
|
|
|
106
96
|
projectKey: decodedToken[publicClaimForProjectKey]
|
|
107
97
|
};
|
|
108
98
|
const userPermissions = decodedToken[publicClaimForUserPermissionsKey];
|
|
109
|
-
|
|
110
99
|
if (Boolean(userPermissions === null || userPermissions === void 0 ? void 0 : userPermissions.length)) {
|
|
111
100
|
request.session.userPermissions = userPermissions;
|
|
112
101
|
}
|
|
113
|
-
}
|
|
114
|
-
|
|
102
|
+
}
|
|
115
103
|
|
|
104
|
+
// Remove the field used by the JWT middleware.
|
|
116
105
|
delete request.decoded_token;
|
|
117
|
-
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Given a cloud identifier, try to map it to one of the supported
|
|
118
109
|
// environments and return the MC API URL for that environment.
|
|
119
110
|
// The URL points to the new hostnames.
|
|
120
111
|
// https://docs.commercetools.com/custom-applications/concepts/merchant-center-api#hostnames
|
|
121
|
-
|
|
122
|
-
|
|
123
112
|
const mapCloudIdentifierToIssuer = issuer => {
|
|
124
113
|
switch (issuer) {
|
|
125
114
|
case CLOUD_IDENTIFIERS.GCP_AU:
|
|
126
115
|
return MC_API_URLS.GCP_AU;
|
|
127
|
-
|
|
128
116
|
case CLOUD_IDENTIFIERS.GCP_EU:
|
|
129
117
|
return MC_API_URLS.GCP_EU;
|
|
130
|
-
|
|
131
118
|
case CLOUD_IDENTIFIERS.GCP_US:
|
|
132
119
|
return MC_API_URLS.GCP_US;
|
|
133
|
-
|
|
134
120
|
case CLOUD_IDENTIFIERS.AWS_FRA:
|
|
135
121
|
return MC_API_URLS.AWS_FRA;
|
|
136
|
-
|
|
137
122
|
case CLOUD_IDENTIFIERS.AWS_OHIO:
|
|
138
123
|
return MC_API_URLS.AWS_OHIO;
|
|
139
|
-
|
|
140
124
|
default:
|
|
141
125
|
return undefined;
|
|
142
126
|
}
|
|
143
|
-
};
|
|
127
|
+
};
|
|
128
|
+
// Given a cloud identifier, try to map it to a legacy hostname.
|
|
144
129
|
// This is for backwards compatibility.
|
|
145
|
-
|
|
146
|
-
|
|
147
130
|
const mapToLegacyIssuer = cloudIdentifier => {
|
|
148
131
|
switch (cloudIdentifier) {
|
|
149
132
|
case CLOUD_IDENTIFIERS.GCP_EU:
|
|
150
133
|
return 'https://mc-api.commercetools.com';
|
|
151
|
-
|
|
152
134
|
case CLOUD_IDENTIFIERS.GCP_US:
|
|
153
135
|
return 'https://mc-api.commercetools.co';
|
|
154
|
-
|
|
155
136
|
default:
|
|
156
137
|
return undefined;
|
|
157
138
|
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
|
|
139
|
+
};
|
|
140
|
+
// Verifies that the issuer is a valid URL.
|
|
161
141
|
const throwIfIssuerIsNotAValidUrl = issuer => {
|
|
162
142
|
try {
|
|
163
143
|
new _URL__default["default"](issuer);
|
|
164
144
|
} catch (error) {
|
|
165
145
|
throw new Error("Invalid issuer URL \"".concat(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/custom-applications/concepts/merchant-center-api#hostnames."));
|
|
166
146
|
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
|
|
147
|
+
};
|
|
148
|
+
// Validates required option values.
|
|
170
149
|
const validateRequiredValues = options => {
|
|
171
150
|
if (!options.audience) {
|
|
172
151
|
throw new Error("Missing required option \"audience\"");
|
|
173
152
|
}
|
|
174
|
-
|
|
175
153
|
if (!options.issuer) {
|
|
176
154
|
throw new Error("Missing required option \"issuer\"");
|
|
177
155
|
}
|
|
178
|
-
};
|
|
156
|
+
};
|
|
157
|
+
// Attempt to parse the given issuer. If the value is a cloud identifier, it will
|
|
179
158
|
// be mapped to one of the supported values. If not, we assume the value is a valid URL.
|
|
180
|
-
|
|
181
|
-
|
|
182
159
|
const getConfiguredDefaultIssuer = options => {
|
|
183
160
|
const issuer = mapCloudIdentifierToIssuer(options.issuer);
|
|
184
|
-
|
|
185
161
|
if (!issuer) {
|
|
186
162
|
throwIfIssuerIsNotAValidUrl(options.issuer);
|
|
187
163
|
return options.issuer;
|
|
188
164
|
}
|
|
189
|
-
|
|
190
165
|
return issuer;
|
|
191
|
-
};
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Construct the audience from the given option + the request path.
|
|
192
169
|
// If the request path is `/`, do not append it to the audience, otherwise
|
|
193
170
|
// the token validation might fail because of mismatching audiences.
|
|
194
|
-
|
|
195
|
-
|
|
196
171
|
const getConfiguredAudience = (options, requestPath) => {
|
|
197
172
|
var _context;
|
|
198
|
-
|
|
199
173
|
// remove the trailing slash
|
|
200
174
|
const url = new _URL__default["default"](_concatInstanceProperty__default["default"](_context = "".concat(options.audience.replace(/\/?$/, ''))).call(_context, requestPath));
|
|
201
|
-
|
|
202
175
|
switch (options.audiencePolicy) {
|
|
203
176
|
case 'forward-url-origin':
|
|
204
177
|
return url.origin;
|
|
205
|
-
|
|
206
178
|
default:
|
|
207
179
|
{
|
|
208
180
|
var _context2;
|
|
209
|
-
|
|
210
181
|
if (requestPath === '/') {
|
|
211
182
|
return url.origin;
|
|
212
183
|
}
|
|
213
|
-
|
|
214
184
|
return _concatInstanceProperty__default["default"](_context2 = "".concat(url.origin)).call(_context2, url.pathname);
|
|
215
185
|
}
|
|
216
186
|
}
|
|
217
187
|
};
|
|
218
|
-
|
|
219
188
|
function createSessionAuthVerifier(options) {
|
|
220
189
|
validateRequiredValues(options);
|
|
221
|
-
const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
|
|
190
|
+
const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
|
|
222
191
|
|
|
192
|
+
// Returns an async HTTP handler.
|
|
223
193
|
return async (request, response) => {
|
|
224
194
|
var _mapCloudIdentifierTo, _request$originalUrl;
|
|
225
|
-
|
|
226
195
|
// Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
|
|
227
196
|
const cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, "Missing \"X-MC-API-Cloud-Identifier\" header.");
|
|
228
|
-
let issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer;
|
|
229
|
-
// The version should be sent by the client making the request, to use the features of v2.
|
|
197
|
+
let issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer;
|
|
230
198
|
|
|
199
|
+
// Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
|
|
200
|
+
// The version should be sent by the client making the request, to use the features of v2.
|
|
231
201
|
const proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, "Missing \"X-MC-API-Forward-To-Version\" header.");
|
|
232
|
-
|
|
233
202
|
if (proxyForwardVersion === 'v1') {
|
|
234
203
|
var _mapToLegacyIssuer;
|
|
235
|
-
|
|
236
204
|
// Fall back to legacy issuer domains
|
|
237
205
|
issuer = (_mapToLegacyIssuer = mapToLegacyIssuer(cloudIdentifierHeader)) !== null && _mapToLegacyIssuer !== void 0 ? _mapToLegacyIssuer : issuer;
|
|
238
206
|
}
|
|
239
|
-
|
|
240
207
|
const requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : (_request$originalUrl = request.originalUrl) !== null && _request$originalUrl !== void 0 ? _request$originalUrl : request.url;
|
|
241
|
-
|
|
242
208
|
if (!requestUrlPath || !_startsWithInstanceProperty__default["default"](requestUrlPath).call(requestUrlPath, '/')) {
|
|
243
209
|
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"));
|
|
244
210
|
}
|
|
245
|
-
|
|
246
211
|
const audience = getConfiguredAudience(options, requestUrlPath);
|
|
247
212
|
return new _Promise__default["default"]((resolve, reject) => {
|
|
248
213
|
expressJwt.expressjwt({
|
|
@@ -261,8 +226,8 @@ function createSessionAuthVerifier(options) {
|
|
|
261
226
|
// Validate the audience and the issuer.
|
|
262
227
|
audience,
|
|
263
228
|
issuer,
|
|
264
|
-
algorithms: ['RS256']
|
|
265
|
-
|
|
229
|
+
algorithms: ['RS256']
|
|
230
|
+
// @ts-ignore: the middleware expects an Express.js Request/Response objects
|
|
266
231
|
})(request, response !== null && response !== void 0 ? response : {}, error => {
|
|
267
232
|
if (error) {
|
|
268
233
|
reject(error);
|
|
@@ -39,42 +39,32 @@ const MC_API_PROXY_HEADERS = {
|
|
|
39
39
|
|
|
40
40
|
const getHeaderByCaseInsensitiveKey = (headers, headerKey) => {
|
|
41
41
|
var _context;
|
|
42
|
-
|
|
43
42
|
const matchingHeader = _findInstanceProperty(_context = _Object$entries(headers)).call(_context, _ref => {
|
|
44
43
|
let _ref2 = _slicedToArray(_ref, 1),
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
key = _ref2[0];
|
|
47
45
|
return headerKey.toLowerCase() === key.toLowerCase();
|
|
48
46
|
});
|
|
49
|
-
|
|
50
47
|
if (matchingHeader && matchingHeader.length > 0) {
|
|
51
48
|
const _matchingHeader = _slicedToArray(matchingHeader, 2),
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
headerValue = _matchingHeader[1];
|
|
54
50
|
return _Array$isArray(headerValue) ? headerValue[0] : headerValue;
|
|
55
51
|
}
|
|
56
|
-
|
|
57
52
|
return undefined;
|
|
58
53
|
};
|
|
59
|
-
|
|
60
54
|
const getFirstHeaderValueOrThrow = (headers, headerKey, errorMessage) => {
|
|
61
55
|
const headerValue = getHeaderByCaseInsensitiveKey(headers, headerKey);
|
|
62
|
-
|
|
63
56
|
if (!headerValue) {
|
|
64
57
|
throw new Error(errorMessage);
|
|
65
58
|
}
|
|
66
|
-
|
|
67
59
|
return headerValue;
|
|
68
60
|
};
|
|
69
61
|
|
|
70
62
|
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; }
|
|
71
|
-
|
|
72
63
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var _context3, _context4; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty(_context3 = ownKeys(Object(source), !0)).call(_context3, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)) : _forEachInstanceProperty(_context4 = ownKeys(Object(source))).call(_context4, function (key) { _Object$defineProperty(target, key, _Object$getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
73
|
-
const decodedTokenKey = 'decoded_token';
|
|
74
|
-
|
|
64
|
+
const decodedTokenKey = 'decoded_token';
|
|
65
|
+
// Assign a session object to the request object.
|
|
75
66
|
const writeSessionContext = request => {
|
|
76
67
|
const decodedToken = request[decodedTokenKey];
|
|
77
|
-
|
|
78
68
|
if (decodedToken) {
|
|
79
69
|
const publicClaimForProjectKey = "".concat(decodedToken.iss, "/claims/project_key");
|
|
80
70
|
const publicClaimForUserPermissionsKey = "".concat(decodedToken.iss, "/claims/user_permissions");
|
|
@@ -83,143 +73,118 @@ const writeSessionContext = request => {
|
|
|
83
73
|
projectKey: decodedToken[publicClaimForProjectKey]
|
|
84
74
|
};
|
|
85
75
|
const userPermissions = decodedToken[publicClaimForUserPermissionsKey];
|
|
86
|
-
|
|
87
76
|
if (Boolean(userPermissions === null || userPermissions === void 0 ? void 0 : userPermissions.length)) {
|
|
88
77
|
request.session.userPermissions = userPermissions;
|
|
89
78
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
79
|
+
}
|
|
92
80
|
|
|
81
|
+
// Remove the field used by the JWT middleware.
|
|
93
82
|
delete request.decoded_token;
|
|
94
|
-
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Given a cloud identifier, try to map it to one of the supported
|
|
95
86
|
// environments and return the MC API URL for that environment.
|
|
96
87
|
// The URL points to the new hostnames.
|
|
97
88
|
// https://docs.commercetools.com/custom-applications/concepts/merchant-center-api#hostnames
|
|
98
|
-
|
|
99
|
-
|
|
100
89
|
const mapCloudIdentifierToIssuer = issuer => {
|
|
101
90
|
switch (issuer) {
|
|
102
91
|
case CLOUD_IDENTIFIERS.GCP_AU:
|
|
103
92
|
return MC_API_URLS.GCP_AU;
|
|
104
|
-
|
|
105
93
|
case CLOUD_IDENTIFIERS.GCP_EU:
|
|
106
94
|
return MC_API_URLS.GCP_EU;
|
|
107
|
-
|
|
108
95
|
case CLOUD_IDENTIFIERS.GCP_US:
|
|
109
96
|
return MC_API_URLS.GCP_US;
|
|
110
|
-
|
|
111
97
|
case CLOUD_IDENTIFIERS.AWS_FRA:
|
|
112
98
|
return MC_API_URLS.AWS_FRA;
|
|
113
|
-
|
|
114
99
|
case CLOUD_IDENTIFIERS.AWS_OHIO:
|
|
115
100
|
return MC_API_URLS.AWS_OHIO;
|
|
116
|
-
|
|
117
101
|
default:
|
|
118
102
|
return undefined;
|
|
119
103
|
}
|
|
120
|
-
};
|
|
104
|
+
};
|
|
105
|
+
// Given a cloud identifier, try to map it to a legacy hostname.
|
|
121
106
|
// This is for backwards compatibility.
|
|
122
|
-
|
|
123
|
-
|
|
124
107
|
const mapToLegacyIssuer = cloudIdentifier => {
|
|
125
108
|
switch (cloudIdentifier) {
|
|
126
109
|
case CLOUD_IDENTIFIERS.GCP_EU:
|
|
127
110
|
return 'https://mc-api.commercetools.com';
|
|
128
|
-
|
|
129
111
|
case CLOUD_IDENTIFIERS.GCP_US:
|
|
130
112
|
return 'https://mc-api.commercetools.co';
|
|
131
|
-
|
|
132
113
|
default:
|
|
133
114
|
return undefined;
|
|
134
115
|
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
|
|
116
|
+
};
|
|
117
|
+
// Verifies that the issuer is a valid URL.
|
|
138
118
|
const throwIfIssuerIsNotAValidUrl = issuer => {
|
|
139
119
|
try {
|
|
140
120
|
new _URL(issuer);
|
|
141
121
|
} catch (error) {
|
|
142
122
|
throw new Error("Invalid issuer URL \"".concat(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/custom-applications/concepts/merchant-center-api#hostnames."));
|
|
143
123
|
}
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
|
|
124
|
+
};
|
|
125
|
+
// Validates required option values.
|
|
147
126
|
const validateRequiredValues = options => {
|
|
148
127
|
if (!options.audience) {
|
|
149
128
|
throw new Error("Missing required option \"audience\"");
|
|
150
129
|
}
|
|
151
|
-
|
|
152
130
|
if (!options.issuer) {
|
|
153
131
|
throw new Error("Missing required option \"issuer\"");
|
|
154
132
|
}
|
|
155
|
-
};
|
|
133
|
+
};
|
|
134
|
+
// Attempt to parse the given issuer. If the value is a cloud identifier, it will
|
|
156
135
|
// be mapped to one of the supported values. If not, we assume the value is a valid URL.
|
|
157
|
-
|
|
158
|
-
|
|
159
136
|
const getConfiguredDefaultIssuer = options => {
|
|
160
137
|
const issuer = mapCloudIdentifierToIssuer(options.issuer);
|
|
161
|
-
|
|
162
138
|
if (!issuer) {
|
|
163
139
|
throwIfIssuerIsNotAValidUrl(options.issuer);
|
|
164
140
|
return options.issuer;
|
|
165
141
|
}
|
|
166
|
-
|
|
167
142
|
return issuer;
|
|
168
|
-
};
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Construct the audience from the given option + the request path.
|
|
169
146
|
// If the request path is `/`, do not append it to the audience, otherwise
|
|
170
147
|
// the token validation might fail because of mismatching audiences.
|
|
171
|
-
|
|
172
|
-
|
|
173
148
|
const getConfiguredAudience = (options, requestPath) => {
|
|
174
149
|
var _context;
|
|
175
|
-
|
|
176
150
|
// remove the trailing slash
|
|
177
151
|
const url = new _URL(_concatInstanceProperty(_context = "".concat(options.audience.replace(/\/?$/, ''))).call(_context, requestPath));
|
|
178
|
-
|
|
179
152
|
switch (options.audiencePolicy) {
|
|
180
153
|
case 'forward-url-origin':
|
|
181
154
|
return url.origin;
|
|
182
|
-
|
|
183
155
|
default:
|
|
184
156
|
{
|
|
185
157
|
var _context2;
|
|
186
|
-
|
|
187
158
|
if (requestPath === '/') {
|
|
188
159
|
return url.origin;
|
|
189
160
|
}
|
|
190
|
-
|
|
191
161
|
return _concatInstanceProperty(_context2 = "".concat(url.origin)).call(_context2, url.pathname);
|
|
192
162
|
}
|
|
193
163
|
}
|
|
194
164
|
};
|
|
195
|
-
|
|
196
165
|
function createSessionAuthVerifier(options) {
|
|
197
166
|
validateRequiredValues(options);
|
|
198
|
-
const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
|
|
167
|
+
const configuredDefaultIssuer = getConfiguredDefaultIssuer(options);
|
|
199
168
|
|
|
169
|
+
// Returns an async HTTP handler.
|
|
200
170
|
return async (request, response) => {
|
|
201
171
|
var _mapCloudIdentifierTo, _request$originalUrl;
|
|
202
|
-
|
|
203
172
|
// Get the cloud identifier header, forwarded by the `/proxy/forward-to` endpoint.
|
|
204
173
|
const cloudIdentifierHeader = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.CLOUD_IDENTIFIER, "Missing \"X-MC-API-Cloud-Identifier\" header.");
|
|
205
|
-
let issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer;
|
|
206
|
-
// The version should be sent by the client making the request, to use the features of v2.
|
|
174
|
+
let issuer = options.inferIssuer && cloudIdentifierHeader ? (_mapCloudIdentifierTo = mapCloudIdentifierToIssuer(cloudIdentifierHeader)) !== null && _mapCloudIdentifierTo !== void 0 ? _mapCloudIdentifierTo : configuredDefaultIssuer : configuredDefaultIssuer;
|
|
207
175
|
|
|
176
|
+
// Get the `Accept-version` header, forwarded by the `/proxy/forward-to` endpoint.
|
|
177
|
+
// The version should be sent by the client making the request, to use the features of v2.
|
|
208
178
|
const proxyForwardVersion = getFirstHeaderValueOrThrow(request.headers, MC_API_PROXY_HEADERS.FORWARD_TO_VERSION, "Missing \"X-MC-API-Forward-To-Version\" header.");
|
|
209
|
-
|
|
210
179
|
if (proxyForwardVersion === 'v1') {
|
|
211
180
|
var _mapToLegacyIssuer;
|
|
212
|
-
|
|
213
181
|
// Fall back to legacy issuer domains
|
|
214
182
|
issuer = (_mapToLegacyIssuer = mapToLegacyIssuer(cloudIdentifierHeader)) !== null && _mapToLegacyIssuer !== void 0 ? _mapToLegacyIssuer : issuer;
|
|
215
183
|
}
|
|
216
|
-
|
|
217
184
|
const requestUrlPath = options.getRequestUrl ? options.getRequestUrl(request) : (_request$originalUrl = request.originalUrl) !== null && _request$originalUrl !== void 0 ? _request$originalUrl : request.url;
|
|
218
|
-
|
|
219
185
|
if (!requestUrlPath || !_startsWithInstanceProperty(requestUrlPath).call(requestUrlPath, '/')) {
|
|
220
186
|
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"));
|
|
221
187
|
}
|
|
222
|
-
|
|
223
188
|
const audience = getConfiguredAudience(options, requestUrlPath);
|
|
224
189
|
return new _Promise((resolve, reject) => {
|
|
225
190
|
expressjwt({
|
|
@@ -238,8 +203,8 @@ function createSessionAuthVerifier(options) {
|
|
|
238
203
|
// Validate the audience and the issuer.
|
|
239
204
|
audience,
|
|
240
205
|
issuer,
|
|
241
|
-
algorithms: ['RS256']
|
|
242
|
-
|
|
206
|
+
algorithms: ['RS256']
|
|
207
|
+
// @ts-ignore: the middleware expects an Express.js Request/Response objects
|
|
243
208
|
})(request, response !== null && response !== void 0 ? response : {}, error => {
|
|
244
209
|
if (error) {
|
|
245
210
|
reject(error);
|
|
@@ -4,10 +4,10 @@ type TDecodedJWT = {
|
|
|
4
4
|
iss: string;
|
|
5
5
|
[property: string]: string | string[];
|
|
6
6
|
};
|
|
7
|
-
declare const writeSessionContext: <
|
|
7
|
+
declare const writeSessionContext: <Request extends TBaseRequest>(request: Request & {
|
|
8
8
|
decoded_token?: TDecodedJWT | undefined;
|
|
9
9
|
session?: TSession | undefined;
|
|
10
10
|
}) => void;
|
|
11
|
-
export declare const getConfiguredAudience: <
|
|
11
|
+
export declare const getConfiguredAudience: <Request extends TBaseRequest>(options: TSessionMiddlewareOptions<Request>, requestPath: string) => string;
|
|
12
12
|
declare function createSessionAuthVerifier<Request extends TBaseRequest>(options: TSessionMiddlewareOptions<Request>): (request: Request, response?: unknown) => Promise<void>;
|
|
13
13
|
export { createSessionAuthVerifier, writeSessionContext };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercetools-backend/express",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.3.0",
|
|
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": {
|
|
@@ -9,17 +9,30 @@
|
|
|
9
9
|
"directory": "packages-backend/express"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://docs.commercetools.com/custom-applications",
|
|
12
|
-
"keywords": [
|
|
12
|
+
"keywords": [
|
|
13
|
+
"javascript",
|
|
14
|
+
"nodejs",
|
|
15
|
+
"express",
|
|
16
|
+
"http",
|
|
17
|
+
"server",
|
|
18
|
+
"toolkit"
|
|
19
|
+
],
|
|
13
20
|
"license": "MIT",
|
|
14
21
|
"publishConfig": {
|
|
15
22
|
"access": "public"
|
|
16
23
|
},
|
|
17
24
|
"main": "dist/commercetools-backend-express.cjs.js",
|
|
18
25
|
"module": "dist/commercetools-backend-express.esm.js",
|
|
19
|
-
"files": [
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"package.json",
|
|
29
|
+
"LICENSE",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
20
32
|
"dependencies": {
|
|
21
33
|
"@babel/runtime": "^7.20.13",
|
|
22
34
|
"@babel/runtime-corejs3": "^7.20.13",
|
|
35
|
+
"@types/express": "^4.17.17",
|
|
23
36
|
"@types/node": "^18.15.11",
|
|
24
37
|
"express": "4.18.2",
|
|
25
38
|
"express-jwt": "8.4.1",
|
|
@@ -32,4 +45,4 @@
|
|
|
32
45
|
"jose": "2.0.6",
|
|
33
46
|
"msw": "0.49.3"
|
|
34
47
|
}
|
|
35
|
-
}
|
|
48
|
+
}
|