UNPKG

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