UNPKG

18 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _defineProperty(obj, key, value) {
6 if (key in obj) {
7 Object.defineProperty(obj, key, {
8 value: value,
9 enumerable: true,
10 configurable: true,
11 writable: true
12 });
13 } else {
14 obj[key] = value;
15 }
16
17 return obj;
18}
19
20function ownKeys(object, enumerableOnly) {
21 var keys = Object.keys(object);
22
23 if (Object.getOwnPropertySymbols) {
24 var symbols = Object.getOwnPropertySymbols(object);
25 if (enumerableOnly) symbols = symbols.filter(function (sym) {
26 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
27 });
28 keys.push.apply(keys, symbols);
29 }
30
31 return keys;
32}
33
34function _objectSpread2(target) {
35 for (var i = 1; i < arguments.length; i++) {
36 var source = arguments[i] != null ? arguments[i] : {};
37
38 if (i % 2) {
39 ownKeys(Object(source), true).forEach(function (key) {
40 _defineProperty(target, key, source[key]);
41 });
42 } else if (Object.getOwnPropertyDescriptors) {
43 Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
44 } else {
45 ownKeys(Object(source)).forEach(function (key) {
46 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
47 });
48 }
49 }
50
51 return target;
52}
53
54// POST https://{host}/oauth/token?grant_type=client_credentials&scope={scope}
55// Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
56function buildRequestForClientCredentialsFlow(options) {
57 if (!options) throw new Error('Missing required options');
58 if (!options.host) throw new Error('Missing required option (host)');
59 if (!options.projectKey) throw new Error('Missing required option (projectKey)');
60 if (!options.credentials) throw new Error('Missing required option (credentials)');
61 var _options$credentials = options.credentials,
62 clientId = _options$credentials.clientId,
63 clientSecret = _options$credentials.clientSecret;
64 if (!(clientId && clientSecret)) throw new Error('Missing required credentials (clientId, clientSecret)');
65 var scope = options.scopes ? options.scopes.join(' ') : undefined;
66 var basicAuth = Buffer.from("".concat(clientId, ":").concat(clientSecret)).toString('base64'); // This is mostly useful for internal testing purposes to be able to check
67 // other oauth endpoints.
68
69 var oauthUri = options.oauthUri || '/oauth/token';
70 var url = options.host.replace(/\/$/, '') + oauthUri;
71 var body = "grant_type=client_credentials".concat(scope ? "&scope=".concat(scope) : '');
72 return {
73 basicAuth: basicAuth,
74 url: url,
75 body: body
76 };
77}
78function buildRequestForPasswordFlow(options) {
79 if (!options) throw new Error('Missing required options');
80 if (!options.host) throw new Error('Missing required option (host)');
81 if (!options.projectKey) throw new Error('Missing required option (projectKey)');
82 if (!options.credentials) throw new Error('Missing required option (credentials)');
83 var _options$credentials2 = options.credentials,
84 clientId = _options$credentials2.clientId,
85 clientSecret = _options$credentials2.clientSecret,
86 user = _options$credentials2.user;
87 var pKey = options.projectKey;
88 if (!(clientId && clientSecret && user)) throw new Error('Missing required credentials (clientId, clientSecret, user)');
89 var username = user.username,
90 password = user.password;
91 if (!(username && password)) throw new Error('Missing required user credentials (username, password)');
92 var scope = (options.scopes || []).join(' ');
93 var scopeStr = scope ? "&scope=".concat(scope) : '';
94 var basicAuth = Buffer.from("".concat(clientId, ":").concat(clientSecret)).toString('base64'); // This is mostly useful for internal testing purposes to be able to check
95 // other oauth endpoints.
96
97 var oauthUri = options.oauthUri || "/oauth/".concat(pKey, "/customers/token");
98 var url = options.host.replace(/\/$/, '') + oauthUri; // eslint-disable-next-line max-len
99 // encode username and password as requested by platform
100
101 var body = "grant_type=password&username=".concat(encodeURIComponent(username), "&password=").concat(encodeURIComponent(password)).concat(scopeStr);
102 return {
103 basicAuth: basicAuth,
104 url: url,
105 body: body
106 };
107}
108function buildRequestForRefreshTokenFlow(options) {
109 if (!options) throw new Error('Missing required options');
110 if (!options.host) throw new Error('Missing required option (host)');
111 if (!options.projectKey) throw new Error('Missing required option (projectKey)');
112 if (!options.credentials) throw new Error('Missing required option (credentials)');
113 if (!options.refreshToken) throw new Error('Missing required option (refreshToken)');
114 var _options$credentials3 = options.credentials,
115 clientId = _options$credentials3.clientId,
116 clientSecret = _options$credentials3.clientSecret;
117 if (!(clientId && clientSecret)) throw new Error('Missing required credentials (clientId, clientSecret)');
118 var basicAuth = Buffer.from("".concat(clientId, ":").concat(clientSecret)).toString('base64'); // This is mostly useful for internal testing purposes to be able to check
119 // other oauth endpoints.
120
121 var oauthUri = options.oauthUri || '/oauth/token';
122 var url = options.host.replace(/\/$/, '') + oauthUri;
123 var body = "grant_type=refresh_token&refresh_token=".concat(encodeURIComponent(options.refreshToken));
124 return {
125 basicAuth: basicAuth,
126 url: url,
127 body: body
128 };
129}
130function buildRequestForAnonymousSessionFlow(options) {
131 if (!options) throw new Error('Missing required options');
132 if (!options.projectKey) throw new Error('Missing required option (projectKey)');
133 var pKey = options.projectKey; // eslint-disable-next-line no-param-reassign
134
135 options.oauthUri = options.oauthUri || "/oauth/".concat(pKey, "/anonymous/token");
136 var result = buildRequestForClientCredentialsFlow(options);
137 if (options.credentials.anonymousId) result.body += "&anonymous_id=".concat(options.credentials.anonymousId);
138 return _objectSpread2({}, result);
139}
140
141function buildTokenCacheKey(options) {
142 return {
143 clientId: options.credentials.clientId,
144 host: options.host,
145 projectKey: options.projectKey
146 };
147}
148
149function mergeAuthHeader(token, req) {
150 return _objectSpread2({}, req, {
151 headers: _objectSpread2({}, req.headers, {
152 Authorization: "Bearer ".concat(token)
153 })
154 });
155}
156
157function calculateExpirationTime(expiresIn) {
158 return Date.now() + expiresIn * 1000 - // Add a gap of 2 hours before expiration time.
159 2 * 60 * 60 * 1000;
160}
161
162function executeRequest(_ref) {
163 var fetcher = _ref.fetcher,
164 url = _ref.url,
165 basicAuth = _ref.basicAuth,
166 body = _ref.body,
167 tokenCache = _ref.tokenCache,
168 requestState = _ref.requestState,
169 pendingTasks = _ref.pendingTasks,
170 response = _ref.response,
171 tokenCacheKey = _ref.tokenCacheKey;
172 fetcher(url, {
173 method: 'POST',
174 headers: {
175 Authorization: "Basic ".concat(basicAuth),
176 'Content-Length': Buffer.byteLength(body).toString(),
177 'Content-Type': 'application/x-www-form-urlencoded'
178 },
179 body: body
180 }).then(function (res) {
181 if (res.ok) return res.json().then(function (_ref2) {
182 var token = _ref2.access_token,
183 expiresIn = _ref2.expires_in,
184 refreshToken = _ref2.refresh_token;
185 var expirationTime = calculateExpirationTime(expiresIn); // Cache new token
186
187 tokenCache.set({
188 token: token,
189 expirationTime: expirationTime,
190 refreshToken: refreshToken
191 }, tokenCacheKey); // Dispatch all pending requests
192
193 requestState.set(false); // Freeze and copy pending queue, reset original one for accepting
194 // new pending tasks
195
196 var executionQueue = pendingTasks.slice(); // eslint-disable-next-line no-param-reassign
197
198 pendingTasks = [];
199 executionQueue.forEach(function (task) {
200 // Assign the new token in the request header
201 var requestWithAuth = mergeAuthHeader(token, task.request); // console.log('test', cache, pendingTasks)
202 // Continue by calling the task's own next function
203
204 task.next(requestWithAuth, task.response);
205 });
206 }); // Handle error response
207
208 return res.text().then(function (text) {
209 var parsed;
210
211 try {
212 parsed = JSON.parse(text);
213 } catch (error) {
214 /* noop */
215 }
216
217 var error = new Error(parsed ? parsed.message : text);
218 if (parsed) error.body = parsed;
219 response.reject(error);
220 });
221 }).catch(function (error) {
222 if (response && typeof response.reject === 'function') response.reject(error);
223 });
224}
225
226function authMiddlewareBase(_ref3, next, userOptions) {
227 var request = _ref3.request,
228 response = _ref3.response,
229 url = _ref3.url,
230 basicAuth = _ref3.basicAuth,
231 body = _ref3.body,
232 pendingTasks = _ref3.pendingTasks,
233 requestState = _ref3.requestState,
234 tokenCache = _ref3.tokenCache,
235 tokenCacheKey = _ref3.tokenCacheKey,
236 fetcher = _ref3.fetch;
237 if (!fetcher && typeof fetch === 'undefined') throw new Error('`fetch` is not available. Please pass in `fetch` as an option or have it globally available.');
238 if (!fetcher) // eslint-disable-next-line
239 fetcher = fetch; // Check if there is already a `Authorization` header in the request.
240 // If so, then go directly to the next middleware.
241
242 if (request.headers && request.headers.authorization || request.headers && request.headers.Authorization) {
243 next(request, response);
244 return;
245 } // If there was a token in the tokenCache, and it's not expired, append
246 // the token in the `Authorization` header.
247
248
249 var tokenObj = tokenCache.get(tokenCacheKey);
250
251 if (tokenObj && tokenObj.token && Date.now() < tokenObj.expirationTime) {
252 var requestWithAuth = mergeAuthHeader(tokenObj.token, request);
253 next(requestWithAuth, response);
254 return;
255 } // Keep pending tasks until a token is fetched
256 // Save next function as well, to call it once the token has been fetched, which prevents
257 // unexpected behaviour in a context in which the next function uses global vars
258 // or Promises to capture the token to hand it to other libraries, e.g. Apollo
259
260
261 pendingTasks.push({
262 request: request,
263 response: response,
264 next: next
265 }); // If a token is currently being fetched, just wait ;)
266
267 if (requestState.get()) return; // Mark that a token is being fetched
268
269 requestState.set(true); // If there was a refreshToken in the tokenCache, and there was an expired
270 // token or no token in the tokenCache, use the refreshToken flow
271
272 if (tokenObj && tokenObj.refreshToken && (!tokenObj.token || tokenObj.token && Date.now() > tokenObj.expirationTime)) {
273 if (!userOptions) throw new Error('Missing required options') // eslint-disable-next-line
274 ;
275 executeRequest(_objectSpread2({
276 fetcher: fetcher
277 }, buildRequestForRefreshTokenFlow(_objectSpread2({}, userOptions, {
278 refreshToken: tokenObj.refreshToken
279 })), {
280 tokenCacheKey: tokenCacheKey,
281 tokenCache: tokenCache,
282 requestState: requestState,
283 pendingTasks: pendingTasks,
284 response: response
285 }));
286 return;
287 } // Token and refreshToken are not present or invalid. Request a new token...
288
289
290 executeRequest({
291 fetcher: fetcher,
292 url: url,
293 basicAuth: basicAuth,
294 body: body,
295 tokenCacheKey: tokenCacheKey,
296 tokenCache: tokenCache,
297 requestState: requestState,
298 pendingTasks: pendingTasks,
299 response: response
300 });
301}
302
303function store(initVal) {
304 var value = initVal;
305 return {
306 get: function get() {
307 return value;
308 },
309 set: function set(val) {
310 value = val;
311 return value;
312 }
313 };
314}
315
316function createAuthMiddlewareForClientCredentialsFlow(options) {
317 var tokenCache = options.tokenCache || store({
318 token: '',
319 expirationTime: -1
320 });
321 var requestState = store(false);
322 var pendingTasks = [];
323 return function (next) {
324 return function (request, response) {
325 // Check if there is already a `Authorization` header in the request.
326 // If so, then go directly to the next middleware.
327 if (request.headers && request.headers.authorization || request.headers && request.headers.Authorization) {
328 next(request, response);
329 return;
330 }
331
332 var params = _objectSpread2({
333 request: request,
334 response: response
335 }, buildRequestForClientCredentialsFlow(options), {
336 pendingTasks: pendingTasks,
337 requestState: requestState,
338 tokenCache: tokenCache,
339 tokenCacheKey: buildTokenCacheKey(options),
340 fetch: options.fetch
341 });
342
343 authMiddlewareBase(params, next);
344 };
345 };
346}
347
348function createAuthMiddlewareForPasswordFlow(options) {
349 var tokenCache = store({});
350 var pendingTasks = [];
351 var requestState = store(false);
352 return function (next) {
353 return function (request, response) {
354 // Check if there is already a `Authorization` header in the request.
355 // If so, then go directly to the next middleware.
356 if (request.headers && request.headers.authorization || request.headers && request.headers.Authorization) {
357 next(request, response);
358 return;
359 }
360
361 var params = _objectSpread2({
362 request: request,
363 response: response
364 }, buildRequestForPasswordFlow(options), {
365 pendingTasks: pendingTasks,
366 requestState: requestState,
367 tokenCache: tokenCache,
368 fetch: options.fetch
369 });
370
371 authMiddlewareBase(params, next, options);
372 };
373 };
374}
375
376function createAuthMiddlewareForRefreshTokenFlow(options) {
377 var tokenCache = store({});
378 var pendingTasks = [];
379 var requestState = store(false);
380 return function (next) {
381 return function (request, response) {
382 // Check if there is already a `Authorization` header in the request.
383 // If so, then go directly to the next middleware.
384 if (request.headers && request.headers.authorization || request.headers && request.headers.Authorization) {
385 next(request, response);
386 return;
387 }
388
389 var params = _objectSpread2({
390 request: request,
391 response: response
392 }, buildRequestForRefreshTokenFlow(options), {
393 pendingTasks: pendingTasks,
394 requestState: requestState,
395 tokenCache: tokenCache,
396 fetch: options.fetch
397 });
398
399 authMiddlewareBase(params, next);
400 };
401 };
402}
403
404function createAuthMiddlewareForAnonymousSessionFlow(options) {
405 var tokenCache = store({});
406 var pendingTasks = [];
407 var requestState = store(false);
408 return function (next) {
409 return function (request, response) {
410 // Check if there is already a `Authorization` header in the request.
411 // If so, then go directly to the next middleware.
412 if (request.headers && request.headers.authorization || request.headers && request.headers.Authorization) {
413 next(request, response);
414 return;
415 }
416
417 var params = _objectSpread2({
418 request: request,
419 response: response
420 }, buildRequestForAnonymousSessionFlow(options), {
421 pendingTasks: pendingTasks,
422 requestState: requestState,
423 tokenCache: tokenCache,
424 fetch: options.fetch
425 });
426
427 authMiddlewareBase(params, next, options);
428 };
429 };
430}
431
432function createAuthMiddlewareWithExistingToken() {
433 var authorization = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
434 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
435 return function (next) {
436 return function (request, response) {
437 if (typeof authorization !== 'string') throw new Error('authorization must be a string');
438 var force = options.force === undefined ? true : options.force;
439 /** The request will not be modified if:
440 * 1. no argument is passed
441 * 2. force is false and authorization header exists
442 */
443
444 if (!authorization || (request.headers && request.headers.authorization || request.headers && request.headers.Authorization) && force === false) {
445 return next(request, response);
446 }
447
448 var requestWithAuth = _objectSpread2({}, request, {
449 headers: _objectSpread2({}, request.headers, {
450 Authorization: authorization
451 })
452 });
453
454 return next(requestWithAuth, response);
455 };
456 };
457}
458
459var MANAGE_PROJECT = 'manage_project';
460var MANAGE_PRODUCTS = 'manage_products';
461var VIEW_PRODUCTS = 'view_products';
462var MANAGE_ORDERS = 'manage_orders';
463var VIEW_ORDERS = 'view_orders';
464var MANAGE_MY_ORDERS = 'manage_my_orders';
465var MANAGE_CUSTOMERS = 'manage_customers';
466var VIEW_CUSTOMERS = 'view_customers';
467var MANAGE_MY_PROFILE = 'manage_my_profile';
468var MANAGE_TYPES = 'manage_types';
469var VIEW_TYPES = 'view_types';
470var MANAGE_PAYMENTS = 'manage_payments';
471var VIEW_PAYMENTS = 'view_payments';
472var CREATE_ANONYMOUS_TOKEN = 'create_anonymous_token';
473var MANAGE_SUBSCRIPTIONS = 'manage_subscriptions';
474
475var scopes = /*#__PURE__*/Object.freeze({
476 __proto__: null,
477 MANAGE_PROJECT: MANAGE_PROJECT,
478 MANAGE_PRODUCTS: MANAGE_PRODUCTS,
479 VIEW_PRODUCTS: VIEW_PRODUCTS,
480 MANAGE_ORDERS: MANAGE_ORDERS,
481 VIEW_ORDERS: VIEW_ORDERS,
482 MANAGE_MY_ORDERS: MANAGE_MY_ORDERS,
483 MANAGE_CUSTOMERS: MANAGE_CUSTOMERS,
484 VIEW_CUSTOMERS: VIEW_CUSTOMERS,
485 MANAGE_MY_PROFILE: MANAGE_MY_PROFILE,
486 MANAGE_TYPES: MANAGE_TYPES,
487 VIEW_TYPES: VIEW_TYPES,
488 MANAGE_PAYMENTS: MANAGE_PAYMENTS,
489 VIEW_PAYMENTS: VIEW_PAYMENTS,
490 CREATE_ANONYMOUS_TOKEN: CREATE_ANONYMOUS_TOKEN,
491 MANAGE_SUBSCRIPTIONS: MANAGE_SUBSCRIPTIONS
492});
493
494exports.createAuthMiddlewareForAnonymousSessionFlow = createAuthMiddlewareForAnonymousSessionFlow;
495exports.createAuthMiddlewareForClientCredentialsFlow = createAuthMiddlewareForClientCredentialsFlow;
496exports.createAuthMiddlewareForPasswordFlow = createAuthMiddlewareForPasswordFlow;
497exports.createAuthMiddlewareForRefreshTokenFlow = createAuthMiddlewareForRefreshTokenFlow;
498exports.createAuthMiddlewareWithExistingToken = createAuthMiddlewareWithExistingToken;
499exports.scopes = scopes;