@webex/plugin-authorization-browser-first-party 3.6.0-next.5 → 3.6.0-next.7

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.
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequ
5
5
  _Object$defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.default = void 0;
8
+ exports.default = exports.Events = void 0;
9
9
  var _apply = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/apply"));
10
10
  var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
11
11
  var _assign = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/assign"));
@@ -33,6 +33,16 @@ var lodash = require('lodash');
33
33
  var OAUTH2_CSRF_TOKEN = 'oauth2-csrf-token';
34
34
  var OAUTH2_CODE_VERIFIER = 'oauth2-code-verifier';
35
35
 
36
+ /**
37
+ * Authorization plugin events
38
+ */
39
+ var Events = exports.Events = {
40
+ /**
41
+ * QR code login events
42
+ */
43
+ qRCodeLogin: 'qRCodeLogin'
44
+ };
45
+
36
46
  /**
37
47
  * Browser support for OAuth2. Automatically parses the URL query for an
38
48
  * authorization code
@@ -76,14 +86,45 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
76
86
  },
77
87
  namespace: 'Credentials',
78
88
  /**
79
- * Stores the interval ID for QR code polling
89
+ * EventEmitter for authorization events
90
+ * @instance
91
+ * @memberof AuthorizationBrowserFirstParty
92
+ * @type {EventEmitter}
93
+ * @public
94
+ */
95
+ eventEmitter: new _events.EventEmitter(),
96
+ /**
97
+ * Stores the timer ID for QR code polling
80
98
  * @instance
81
99
  * @memberof AuthorizationBrowserFirstParty
82
100
  * @type {?number}
83
101
  * @private
84
102
  */
85
- pollingRequest: null,
86
- eventEmitter: new _events.EventEmitter(),
103
+ pollingTimer: null,
104
+ /**
105
+ * Stores the expiration timer ID for QR code polling
106
+ * @instance
107
+ * @memberof AuthorizationBrowserFirstParty
108
+ * @type {?number}
109
+ * @private
110
+ */
111
+ pollingExpirationTimer: null,
112
+ /**
113
+ * Monotonically increasing id to identify the current polling request
114
+ * @instance
115
+ * @memberof AuthorizationBrowserFirstParty
116
+ * @type {number}
117
+ * @private
118
+ */
119
+ pollingId: 0,
120
+ /**
121
+ * Identifier for the current polling request
122
+ * @instance
123
+ * @memberof AuthorizationBrowserFirstParty
124
+ * @type {?number}
125
+ * @private
126
+ */
127
+ currentPollingId: null,
87
128
  /**
88
129
  * Initializer
89
130
  * @instance
@@ -241,6 +282,28 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
241
282
  return _promise.default.reject(new ErrorConstructor(res._res || res));
242
283
  });
243
284
  },
285
+ /**
286
+ * Generate a QR code URL to launch the Webex app when scanning with the camera
287
+ * @instance
288
+ * @memberof AuthorizationBrowserFirstParty
289
+ * @param {String} verificationUrl
290
+ * @returns {String}
291
+ */
292
+ _generateQRCodeVerificationUrl: function _generateQRCodeVerificationUrl(verificationUrl) {
293
+ var baseUrl = 'https://web.webex.com/deviceAuth';
294
+ var urlParams = new URLSearchParams(new URL(verificationUrl).search);
295
+ var userCode = urlParams.get('userCode');
296
+ if (userCode) {
297
+ var services = this.webex.internal.services;
298
+ var oauthHelperUrl = services.get('oauth-helper');
299
+ var newVerificationUrl = new URL(baseUrl);
300
+ newVerificationUrl.searchParams.set('usercode', userCode);
301
+ newVerificationUrl.searchParams.set('oauthhelper', oauthHelperUrl);
302
+ return newVerificationUrl.toString();
303
+ } else {
304
+ return verificationUrl;
305
+ }
306
+ },
244
307
  /**
245
308
  * Get an OAuth Login URL for QRCode. Generate QR code based on the returned URL.
246
309
  * @instance
@@ -249,8 +312,8 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
249
312
  */
250
313
  initQRCodeLogin: function initQRCodeLogin() {
251
314
  var _this3 = this;
252
- if (this.pollingRequest) {
253
- this.eventEmitter.emit('qRCodeLogin', {
315
+ if (this.pollingTimer) {
316
+ this.eventEmitter.emit(Events.qRCodeLogin, {
254
317
  eventType: 'getUserCodeFailure',
255
318
  data: {
256
319
  message: 'There is already a polling request'
@@ -276,18 +339,19 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
276
339
  user_code = _res$body.user_code,
277
340
  verification_uri = _res$body.verification_uri,
278
341
  verification_uri_complete = _res$body.verification_uri_complete;
279
- _this3.eventEmitter.emit('qRCodeLogin', {
342
+ var verificationUriComplete = _this3._generateQRCodeVerificationUrl(verification_uri_complete);
343
+ _this3.eventEmitter.emit(Events.qRCodeLogin, {
280
344
  eventType: 'getUserCodeSuccess',
281
345
  userData: {
282
346
  userCode: user_code,
283
347
  verificationUri: verification_uri,
284
- verificationUriComplete: verification_uri_complete
348
+ verificationUriComplete: verificationUriComplete
285
349
  }
286
350
  });
287
351
  // if device authorization success, then start to poll server to check whether the user has completed authorization
288
352
  _this3._startQRCodePolling(res.body);
289
353
  }).catch(function (res) {
290
- _this3.eventEmitter.emit('qRCodeLogin', {
354
+ _this3.eventEmitter.emit(Events.qRCodeLogin, {
291
355
  eventType: 'getUserCodeFailure',
292
356
  data: res.body
293
357
  });
@@ -301,10 +365,11 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
301
365
  * @emits #qRCodeLogin
302
366
  */
303
367
  _startQRCodePolling: function _startQRCodePolling() {
304
- var _this4 = this;
368
+ var _options$interval,
369
+ _this4 = this;
305
370
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
306
371
  if (!options.device_code) {
307
- this.eventEmitter.emit('qRCodeLogin', {
372
+ this.eventEmitter.emit(Events.qRCodeLogin, {
308
373
  eventType: 'authorizationFailure',
309
374
  data: {
310
375
  message: 'A deviceCode is required'
@@ -312,8 +377,8 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
312
377
  });
313
378
  return;
314
379
  }
315
- if (this.pollingRequest) {
316
- this.eventEmitter.emit('qRCodeLogin', {
380
+ if (this.pollingTimer) {
381
+ this.eventEmitter.emit(Events.qRCodeLogin, {
317
382
  eventType: 'authorizationFailure',
318
383
  data: {
319
384
  message: 'There is already a polling request'
@@ -322,15 +387,21 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
322
387
  return;
323
388
  }
324
389
  var deviceCode = options.device_code,
325
- _options$interval = options.interval,
326
- interval = _options$interval === void 0 ? 2 : _options$interval,
327
390
  _options$expires_in = options.expires_in,
328
391
  expiresIn = _options$expires_in === void 0 ? 300 : _options$expires_in;
329
- var attempts = 0;
330
- var maxAttempts = expiresIn / interval;
331
- this.pollingRequest = setInterval(function () {
332
- attempts += 1;
333
- var currentAttempts = attempts;
392
+ var interval = (_options$interval = options.interval) !== null && _options$interval !== void 0 ? _options$interval : 2;
393
+ this.pollingExpirationTimer = setTimeout(function () {
394
+ _this4.cancelQRCodePolling(false);
395
+ _this4.eventEmitter.emit(Events.qRCodeLogin, {
396
+ eventType: 'authorizationFailure',
397
+ data: {
398
+ message: 'Authorization timed out'
399
+ }
400
+ });
401
+ }, expiresIn * 1000);
402
+ var polling = function polling() {
403
+ _this4.pollingId += 1;
404
+ _this4.currentPollingId = _this4.pollingId;
334
405
  _this4.webex.request({
335
406
  method: 'POST',
336
407
  service: 'oauth-helper',
@@ -346,40 +417,45 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
346
417
  sendImmediately: true
347
418
  }
348
419
  }).then(function (res) {
349
- if (_this4.pollingRequest === null) return;
350
- _this4.eventEmitter.emit('qRCodeLogin', {
420
+ // if the pollingId has changed, it means that the polling request has been canceled
421
+ if (_this4.currentPollingId !== _this4.pollingId) return;
422
+ _this4.eventEmitter.emit(Events.qRCodeLogin, {
351
423
  eventType: 'authorizationSuccess',
352
424
  data: res.body
353
425
  });
354
426
  _this4.cancelQRCodePolling();
355
427
  }).catch(function (res) {
356
- if (_this4.pollingRequest === null) return;
357
- if (currentAttempts >= maxAttempts) {
358
- _this4.eventEmitter.emit('qRCodeLogin', {
359
- eventType: 'authorizationFailure',
360
- data: {
361
- message: 'Authorization timed out'
362
- }
363
- });
364
- _this4.cancelQRCodePolling();
428
+ // if the pollingId has changed, it means that the polling request has been canceled
429
+ if (_this4.currentPollingId !== _this4.pollingId) return;
430
+
431
+ // When server sends 400 status code with message 'slow_down', it means that last request happened too soon.
432
+ // So, skip one interval and then poll again.
433
+ if (res.statusCode === 400 && res.body.message === 'slow_down') {
434
+ schedulePolling(interval * 2);
365
435
  return;
366
436
  }
437
+
367
438
  // if the statusCode is 428 which means that the authorization request is still pending
368
439
  // as the end user hasn't yet completed the user-interaction steps. So keep polling.
369
440
  if (res.statusCode === 428) {
370
- _this4.eventEmitter.emit('qRCodeLogin', {
441
+ _this4.eventEmitter.emit(Events.qRCodeLogin, {
371
442
  eventType: 'authorizationPending',
372
443
  data: res.body
373
444
  });
445
+ schedulePolling(interval);
374
446
  return;
375
447
  }
376
448
  _this4.cancelQRCodePolling();
377
- _this4.eventEmitter.emit('qRCodeLogin', {
449
+ _this4.eventEmitter.emit(Events.qRCodeLogin, {
378
450
  eventType: 'authorizationFailure',
379
451
  data: res.body
380
452
  });
381
453
  });
382
- }, interval * 1000);
454
+ };
455
+ var schedulePolling = function schedulePolling(interval) {
456
+ return _this4.pollingTimer = setTimeout(polling, interval * 1000);
457
+ };
458
+ schedulePolling(interval);
383
459
  },
384
460
  /**
385
461
  * cancel polling request
@@ -388,13 +464,17 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
388
464
  * @returns {void}
389
465
  */
390
466
  cancelQRCodePolling: function cancelQRCodePolling() {
391
- if (this.pollingRequest) {
392
- clearInterval(this.pollingRequest);
393
- this.eventEmitter.emit('qRCodeLogin', {
467
+ var withCancelEvent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
468
+ if (this.pollingTimer && withCancelEvent) {
469
+ this.eventEmitter.emit(Events.qRCodeLogin, {
394
470
  eventType: 'pollingCanceled'
395
471
  });
396
- this.pollingRequest = null;
397
472
  }
473
+ this.currentPollingId = null;
474
+ clearTimeout(this.pollingExpirationTimer);
475
+ this.pollingExpirationTimer = null;
476
+ clearTimeout(this.pollingTimer);
477
+ this.pollingTimer = null;
398
478
  },
399
479
  /**
400
480
  * Extracts the orgId from the returned code from idbroker
@@ -504,7 +584,7 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
504
584
  throw new Error("CSRF token ".concat(token, " does not match stored token ").concat(sessionToken));
505
585
  }
506
586
  },
507
- version: "3.6.0-next.5"
587
+ version: "3.6.0-next.7"
508
588
  }, ((0, _applyDecoratedDescriptor2.default)(_obj, "initiateAuthorizationCodeGrant", [_dec], (0, _getOwnPropertyDescriptor.default)(_obj, "initiateAuthorizationCodeGrant"), _obj), (0, _applyDecoratedDescriptor2.default)(_obj, "requestAuthorizationCodeGrant", [_dec2, _common.oneFlight], (0, _getOwnPropertyDescriptor.default)(_obj, "requestAuthorizationCodeGrant"), _obj)), _obj)));
509
589
  var _default = exports.default = Authorization;
510
590
  //# sourceMappingURL=authorization.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_querystring","_interopRequireDefault","require","_url","_events","_common","_webexCore","_lodash","_uuid","_encBase64url","_cryptoJs","_dec","_dec2","_obj","lodash","OAUTH2_CSRF_TOKEN","OAUTH2_CODE_VERIFIER","Authorization","WebexPlugin","extend","whileInFlight","derived","isAuthenticating","deps","fn","isAuthorizing","session","default","type","ready","namespace","pollingRequest","eventEmitter","EventEmitter","initialize","_this","_len","arguments","length","attrs","Array","_key","ret","_apply","prototype","location","url","parse","webex","getWindow","href","_checkForErrors","code","query","state","JSON","base64","decode","codeVerifier","sessionStorage","getItem","removeItem","emailhash","_verifySecurityToken","_cleanUrl","preauthCatalogParams","orgId","_extractOrgIdFromCode","process","nextTick","internal","services","collectPreauthCatalog","catch","_promise","resolve","then","requestAuthorizationCodeGrant","error","logger","warn","initiateLogin","options","undefined","cloneDeep","email","emailHash","CryptoJS","SHA256","toString","csrf_token","_generateSecurityToken","code_challenge","_generateCodeChallenge","code_challenge_method","initiateAuthorizationCodeGrant","info","credentials","buildLoginUrl","_assign","response_type","logout","noRedirect","buildLogoutUrl","_this2","reject","Error","form","grant_type","redirect_uri","config","self_contained_token","code_verifier","request","method","uri","tokenUrl","auth","user","client_id","pass","client_secret","sendImmediately","shouldRefreshAccessToken","res","set","supertoken","body","statusCode","ErrorConstructor","grantErrors","select","_res","initQRCodeLogin","_this3","emit","eventType","data","message","service","resource","scope","_res$body","user_code","verification_uri","verification_uri_complete","userData","userCode","verificationUri","verificationUriComplete","_startQRCodePolling","_this4","device_code","deviceCode","_options$interval","interval","_options$expires_in","expires_in","expiresIn","attempts","maxAttempts","setInterval","currentAttempts","cancelQRCodePolling","clearInterval","split","history","replaceState","_deleteProperty","isEmpty","omit","encode","_stringify","search","querystring","stringify","format","safeCharacterMap","base64url","_safe_map","times","random","join","codeChallenge","setItem","token","uuid","v4","sessionToken","concat","version","_applyDecoratedDescriptor2","_getOwnPropertyDescriptor","oneFlight","_default","exports"],"sources":["authorization.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint camelcase: [0] */\n\nimport querystring from 'querystring';\nimport url from 'url';\nimport {EventEmitter} from 'events';\n\nimport {base64, oneFlight, whileInFlight} from '@webex/common';\nimport {grantErrors, WebexPlugin} from '@webex/webex-core';\nimport {cloneDeep, isEmpty, omit} from 'lodash';\nimport uuid from 'uuid';\nimport base64url from 'crypto-js/enc-base64url';\nimport CryptoJS from 'crypto-js';\n\n// Necessary to require lodash this way in order to stub\n// methods in the unit test\nconst lodash = require('lodash');\n\nconst OAUTH2_CSRF_TOKEN = 'oauth2-csrf-token';\nconst OAUTH2_CODE_VERIFIER = 'oauth2-code-verifier';\n\n/**\n * Browser support for OAuth2. Automatically parses the URL query for an\n * authorization code\n *\n * Use of this plugin for anything other than the Webex Web Client is strongly\n * discouraged and may be broken at any time\n * @class\n * @name AuthorizationBrowserFirstParty\n * @private\n */\nconst Authorization = WebexPlugin.extend({\n derived: {\n /**\n * Alias of {@link AuthorizationBrowserFirstParty#isAuthorizing}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthenticating: {\n deps: ['isAuthorizing'],\n fn() {\n return this.isAuthorizing;\n },\n },\n },\n\n session: {\n /**\n * Indicates if an Authorization Code exchange is inflight\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthorizing: {\n default: false,\n type: 'boolean',\n },\n ready: {\n default: false,\n type: 'boolean',\n },\n },\n\n namespace: 'Credentials',\n\n\n /**\n * Stores the interval ID for QR code polling\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {?number}\n * @private\n */\n pollingRequest: null,\n\n eventEmitter: new EventEmitter(),\n\n /**\n * Initializer\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Authorization}\n */\n // eslint-disable-next-line complexity\n initialize(...attrs) {\n const ret = Reflect.apply(WebexPlugin.prototype.initialize, this, attrs);\n const location = url.parse(this.webex.getWindow().location.href, true);\n\n this._checkForErrors(location);\n\n const {code} = location.query;\n\n if (!code) {\n this.ready = true;\n\n return ret;\n }\n\n if (location.query.state) {\n location.query.state = JSON.parse(base64.decode(location.query.state));\n } else {\n location.query.state = {};\n }\n\n const codeVerifier = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CODE_VERIFIER);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CODE_VERIFIER);\n\n const {emailhash} = location.query.state;\n\n this._verifySecurityToken(location.query);\n this._cleanUrl(location);\n\n let preauthCatalogParams;\n\n const orgId = this._extractOrgIdFromCode(code);\n\n if (emailhash) {\n preauthCatalogParams = {emailhash};\n } else if (orgId) {\n preauthCatalogParams = {orgId};\n }\n\n // Wait until nextTick in case `credentials` hasn't initialized yet\n process.nextTick(() => {\n this.webex.internal.services\n .collectPreauthCatalog(preauthCatalogParams)\n .catch(() => Promise.resolve())\n .then(() => this.requestAuthorizationCodeGrant({code, codeVerifier}))\n .catch((error) => {\n this.logger.warn('authorization: failed initial authorization code grant request', error);\n })\n .then(() => {\n this.ready = true;\n });\n });\n\n return ret;\n },\n\n /**\n * Kicks off an oauth flow\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateLogin(options = {}) {\n options = cloneDeep(options);\n if (options.email) {\n options.emailHash = CryptoJS.SHA256(options.email).toString();\n }\n delete options.email;\n options.state = options.state || {};\n options.state.csrf_token = this._generateSecurityToken();\n // catalog uses emailhash and redirectCI uses emailHash\n options.state.emailhash = options.emailHash;\n\n options.code_challenge = this._generateCodeChallenge();\n options.code_challenge_method = 'S256';\n\n return this.initiateAuthorizationCodeGrant(options);\n },\n\n @whileInFlight('isAuthorizing')\n /**\n * Kicks off the Implicit Code grant flow. Typically called via\n * {@link AuthorizationBrowserFirstParty#initiateLogin}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateAuthorizationCodeGrant(options) {\n this.logger.info('authorization: initiating authorization code grant flow');\n this.webex.getWindow().location = this.webex.credentials.buildLoginUrl(\n Object.assign({response_type: 'code'}, options)\n );\n\n return Promise.resolve();\n },\n\n /**\n * Called by {@link WebexCore#logout()}. Redirects to the logout page\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {boolean} options.noRedirect if true, does not redirect\n * @returns {Promise}\n */\n logout(options = {}) {\n if (!options.noRedirect) {\n this.webex.getWindow().location = this.webex.credentials.buildLogoutUrl(options);\n }\n },\n\n @whileInFlight('isAuthorizing')\n @oneFlight\n /**\n * Exchanges an authorization code for an access token\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {Object} options.code\n * @returns {Promise}\n */\n requestAuthorizationCodeGrant(options = {}) {\n this.logger.info('credentials: requesting authorization code grant');\n\n if (!options.code) {\n return Promise.reject(new Error('`options.code` is required'));\n }\n\n const form = {\n grant_type: 'authorization_code',\n redirect_uri: this.config.redirect_uri,\n code: options.code,\n self_contained_token: true,\n };\n\n if (options.codeVerifier) {\n form.code_verifier = options.codeVerifier;\n }\n\n return this.webex\n .request({\n method: 'POST',\n uri: this.config.tokenUrl,\n form,\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true,\n },\n shouldRefreshAccessToken: false,\n })\n .then((res) => {\n this.webex.credentials.set({supertoken: res.body});\n })\n .catch((res) => {\n if (res.statusCode !== 400) {\n return Promise.reject(res);\n }\n\n const ErrorConstructor = grantErrors.select(res.body.error);\n\n return Promise.reject(new ErrorConstructor(res._res || res));\n });\n },\n\n /**\n * Get an OAuth Login URL for QRCode. Generate QR code based on the returned URL.\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @emits #qRCodeLogin\n */\n initQRCodeLogin() {\n if (this.pollingRequest) {\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'getUserCodeFailure',\n data: {message: 'There is already a polling request'},\n });\n return;\n }\n\n this.webex\n .request({\n method: 'POST',\n service: 'oauth-helper',\n resource: '/actions/device/authorize',\n form: {\n client_id: this.config.client_id,\n scope: this.config.scope,\n },\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true,\n },\n })\n .then((res) => {\n const {user_code, verification_uri, verification_uri_complete} = res.body;\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'getUserCodeSuccess',\n userData: {\n userCode: user_code,\n verificationUri: verification_uri,\n verificationUriComplete: verification_uri_complete,\n }\n });\n // if device authorization success, then start to poll server to check whether the user has completed authorization\n this._startQRCodePolling(res.body);\n })\n .catch((res) => {\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'getUserCodeFailure',\n data: res.body,\n });\n });\n },\n\n /**\n * Polling the server to check whether the user has completed authorization\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @emits #qRCodeLogin\n */\n _startQRCodePolling(options = {}) {\n if (!options.device_code) {\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'authorizationFailure',\n data: {message: 'A deviceCode is required'},\n });\n return;\n }\n\n if (this.pollingRequest) {\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'authorizationFailure',\n data: {message: 'There is already a polling request'},\n });\n return;\n }\n\n const {device_code: deviceCode, interval = 2, expires_in: expiresIn = 300} = options;\n\n let attempts = 0;\n const maxAttempts = expiresIn / interval;\n\n this.pollingRequest = setInterval(() => {\n attempts += 1;\n\n const currentAttempts = attempts;\n this.webex\n .request({\n method: 'POST',\n service: 'oauth-helper',\n resource: '/actions/device/token',\n form: {\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: this.config.client_id,\n },\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true,\n },\n })\n .then((res) => {\n if (this.pollingRequest === null) return;\n\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'authorizationSuccess',\n data: res.body,\n });\n this.cancelQRCodePolling();\n })\n .catch((res) => {\n if (this.pollingRequest === null) return;\n\n if (currentAttempts >= maxAttempts) {\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'authorizationFailure',\n data: {message: 'Authorization timed out'}\n });\n this.cancelQRCodePolling();\n return;\n }\n // if the statusCode is 428 which means that the authorization request is still pending\n // as the end user hasn't yet completed the user-interaction steps. So keep polling.\n if (res.statusCode === 428) {\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'authorizationPending',\n data: res.body\n });\n return;\n }\n\n this.cancelQRCodePolling();\n\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'authorizationFailure',\n data: res.body\n });\n });\n }, interval * 1000);\n },\n\n /**\n * cancel polling request\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @returns {void}\n */\n cancelQRCodePolling() {\n if (this.pollingRequest) {\n clearInterval(this.pollingRequest);\n this.eventEmitter.emit('qRCodeLogin', {\n eventType: 'pollingCanceled',\n });\n this.pollingRequest = null;\n }\n },\n\n /**\n * Extracts the orgId from the returned code from idbroker\n * Description of how to parse the code can be found here:\n * https://wiki.cisco.com/display/IDENTITY/Federated+Token+Validation\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {String} code\n * @private\n * @returns {String}\n */\n _extractOrgIdFromCode(code) {\n return code?.split('_')[2] || undefined;\n },\n\n /**\n * Checks if the result of the login redirect contains an error string\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _checkForErrors(location) {\n const {query} = location;\n\n if (query && query.error) {\n const ErrorConstructor = grantErrors.select(query.error);\n\n throw new ErrorConstructor(query);\n }\n },\n\n /**\n * Removes no-longer needed values from the url (access token, csrf token, etc)\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _cleanUrl(location) {\n location = cloneDeep(location);\n if (this.webex.getWindow().history && this.webex.getWindow().history.replaceState) {\n Reflect.deleteProperty(location.query, 'code');\n if (isEmpty(omit(location.query.state, 'csrf_token'))) {\n Reflect.deleteProperty(location.query, 'state');\n } else {\n location.query.state = base64.encode(\n JSON.stringify(omit(location.query.state, 'csrf_token'))\n );\n }\n location.search = querystring.stringify(location.query);\n Reflect.deleteProperty(location, 'query');\n this.webex.getWindow().history.replaceState({}, null, url.format(location));\n }\n },\n\n /**\n * Generates PKCE code verifier and code challenge and sets the the code verifier in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {string}\n */\n _generateCodeChallenge() {\n this.logger.info('authorization: generating PKCE code challenge');\n\n // eslint-disable-next-line no-underscore-dangle\n const safeCharacterMap = base64url._safe_map;\n\n const codeVerifier = lodash\n .times(128, () => safeCharacterMap[lodash.random(0, safeCharacterMap.length - 1)])\n .join('');\n\n const codeChallenge = CryptoJS.SHA256(codeVerifier).toString(base64url);\n\n this.webex.getWindow().sessionStorage.setItem(OAUTH2_CODE_VERIFIER, codeVerifier);\n\n return codeChallenge;\n },\n\n /**\n * Generates a CSRF token and sticks in in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Promise}\n */\n _generateSecurityToken() {\n this.logger.info('authorization: generating csrf token');\n\n const token = uuid.v4();\n\n this.webex.getWindow().sessionStorage.setItem('oauth2-csrf-token', token);\n\n return token;\n },\n\n /**\n * Checks if the CSRF token in sessionStorage is the same as the one returned\n * in the url.\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} query\n * @private\n * @returns {Promise}\n */\n _verifySecurityToken(query) {\n const sessionToken = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CSRF_TOKEN);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CSRF_TOKEN);\n if (!sessionToken) {\n return;\n }\n\n if (!query.state) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n if (!query.state.csrf_token) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n const token = query.state.csrf_token;\n\n if (token !== sessionToken) {\n throw new Error(`CSRF token ${token} does not match stored token ${sessionToken}`);\n }\n },\n});\n\nexport default Authorization;\n"],"mappings":";;;;;;;;;;;;;;;AAMA,IAAAA,YAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,IAAA,GAAAF,sBAAA,CAAAC,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AAEA,IAAAG,OAAA,GAAAH,OAAA;AACA,IAAAI,UAAA,GAAAJ,OAAA;AACA,IAAAK,OAAA,GAAAL,OAAA;AACA,IAAAM,KAAA,GAAAP,sBAAA,CAAAC,OAAA;AACA,IAAAO,aAAA,GAAAR,sBAAA,CAAAC,OAAA;AACA,IAAAQ,SAAA,GAAAT,sBAAA,CAAAC,OAAA;AAAiC,IAAAS,IAAA,EAAAC,KAAA,EAAAC,IAAA;AAfjC;AACA;AACA;AAEA;AAaA;AACA;AACA,IAAMC,MAAM,GAAGZ,OAAO,CAAC,QAAQ,CAAC;AAEhC,IAAMa,iBAAiB,GAAG,mBAAmB;AAC7C,IAAMC,oBAAoB,GAAG,sBAAsB;;AAEnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAMC,aAAa,GAAGC,sBAAW,CAACC,MAAM,EAAAR,IAAA,GAuIrC,IAAAS,qBAAa,EAAC,eAAe,CAAC,EAAAR,KAAA,GAgC9B,IAAAQ,qBAAa,EAAC,eAAe,CAAC,GAAAP,IAAA,GAvKQ;EACvCQ,OAAO,EAAE;IACP;AACJ;AACA;AACA;AACA;AACA;IACIC,gBAAgB,EAAE;MAChBC,IAAI,EAAE,CAAC,eAAe,CAAC;MACvBC,EAAE,WAAAA,GAAA,EAAG;QACH,OAAO,IAAI,CAACC,aAAa;MAC3B;IACF;EACF,CAAC;EAEDC,OAAO,EAAE;IACP;AACJ;AACA;AACA;AACA;AACA;IACID,aAAa,EAAE;MACbE,OAAO,EAAE,KAAK;MACdC,IAAI,EAAE;IACR,CAAC;IACDC,KAAK,EAAE;MACLF,OAAO,EAAE,KAAK;MACdC,IAAI,EAAE;IACR;EACF,CAAC;EAEDE,SAAS,EAAE,aAAa;EAGxB;AACF;AACA;AACA;AACA;AACA;AACA;EACEC,cAAc,EAAE,IAAI;EAEpBC,YAAY,EAAE,IAAIC,oBAAY,CAAC,CAAC;EAEhC;AACF;AACA;AACA;AACA;AACA;AACA;EACE;EACAC,UAAU,WAAAA,WAAA,EAAW;IAAA,IAAAC,KAAA;IAAA,SAAAC,IAAA,GAAAC,SAAA,CAAAC,MAAA,EAAPC,KAAK,OAAAC,KAAA,CAAAJ,IAAA,GAAAK,IAAA,MAAAA,IAAA,GAAAL,IAAA,EAAAK,IAAA;MAALF,KAAK,CAAAE,IAAA,IAAAJ,SAAA,CAAAI,IAAA;IAAA;IACjB,IAAMC,GAAG,GAAG,IAAAC,MAAA,CAAAhB,OAAA,EAAcT,sBAAW,CAAC0B,SAAS,CAACV,UAAU,EAAE,IAAI,EAAEK,KAAK,CAAC;IACxE,IAAMM,QAAQ,GAAGC,YAAG,CAACC,KAAK,CAAC,IAAI,CAACC,KAAK,CAACC,SAAS,CAAC,CAAC,CAACJ,QAAQ,CAACK,IAAI,EAAE,IAAI,CAAC;IAEtE,IAAI,CAACC,eAAe,CAACN,QAAQ,CAAC;IAE9B,IAAOO,IAAI,GAAIP,QAAQ,CAACQ,KAAK,CAAtBD,IAAI;IAEX,IAAI,CAACA,IAAI,EAAE;MACT,IAAI,CAACvB,KAAK,GAAG,IAAI;MAEjB,OAAOa,GAAG;IACZ;IAEA,IAAIG,QAAQ,CAACQ,KAAK,CAACC,KAAK,EAAE;MACxBT,QAAQ,CAACQ,KAAK,CAACC,KAAK,GAAGC,IAAI,CAACR,KAAK,CAACS,cAAM,CAACC,MAAM,CAACZ,QAAQ,CAACQ,KAAK,CAACC,KAAK,CAAC,CAAC;IACxE,CAAC,MAAM;MACLT,QAAQ,CAACQ,KAAK,CAACC,KAAK,GAAG,CAAC,CAAC;IAC3B;IAEA,IAAMI,YAAY,GAAG,IAAI,CAACV,KAAK,CAACC,SAAS,CAAC,CAAC,CAACU,cAAc,CAACC,OAAO,CAAC5C,oBAAoB,CAAC;IAExF,IAAI,CAACgC,KAAK,CAACC,SAAS,CAAC,CAAC,CAACU,cAAc,CAACE,UAAU,CAAC7C,oBAAoB,CAAC;IAEtE,IAAO8C,SAAS,GAAIjB,QAAQ,CAACQ,KAAK,CAACC,KAAK,CAAjCQ,SAAS;IAEhB,IAAI,CAACC,oBAAoB,CAAClB,QAAQ,CAACQ,KAAK,CAAC;IACzC,IAAI,CAACW,SAAS,CAACnB,QAAQ,CAAC;IAExB,IAAIoB,oBAAoB;IAExB,IAAMC,KAAK,GAAG,IAAI,CAACC,qBAAqB,CAACf,IAAI,CAAC;IAE9C,IAAIU,SAAS,EAAE;MACbG,oBAAoB,GAAG;QAACH,SAAS,EAATA;MAAS,CAAC;IACpC,CAAC,MAAM,IAAII,KAAK,EAAE;MAChBD,oBAAoB,GAAG;QAACC,KAAK,EAALA;MAAK,CAAC;IAChC;;IAEA;IACAE,OAAO,CAACC,QAAQ,CAAC,YAAM;MACrBlC,KAAI,CAACa,KAAK,CAACsB,QAAQ,CAACC,QAAQ,CACzBC,qBAAqB,CAACP,oBAAoB,CAAC,CAC3CQ,KAAK,CAAC;QAAA,OAAMC,QAAA,CAAA/C,OAAA,CAAQgD,OAAO,CAAC,CAAC;MAAA,EAAC,CAC9BC,IAAI,CAAC;QAAA,OAAMzC,KAAI,CAAC0C,6BAA6B,CAAC;UAACzB,IAAI,EAAJA,IAAI;UAAEM,YAAY,EAAZA;QAAY,CAAC,CAAC;MAAA,EAAC,CACpEe,KAAK,CAAC,UAACK,KAAK,EAAK;QAChB3C,KAAI,CAAC4C,MAAM,CAACC,IAAI,CAAC,gEAAgE,EAAEF,KAAK,CAAC;MAC3F,CAAC,CAAC,CACDF,IAAI,CAAC,YAAM;QACVzC,KAAI,CAACN,KAAK,GAAG,IAAI;MACnB,CAAC,CAAC;IACN,CAAC,CAAC;IAEF,OAAOa,GAAG;EACZ,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;EACEuC,aAAa,WAAAA,cAAA,EAAe;IAAA,IAAdC,OAAO,GAAA7C,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAA8C,SAAA,GAAA9C,SAAA,MAAG,CAAC,CAAC;IACxB6C,OAAO,GAAG,IAAAE,iBAAS,EAACF,OAAO,CAAC;IAC5B,IAAIA,OAAO,CAACG,KAAK,EAAE;MACjBH,OAAO,CAACI,SAAS,GAAGC,iBAAQ,CAACC,MAAM,CAACN,OAAO,CAACG,KAAK,CAAC,CAACI,QAAQ,CAAC,CAAC;IAC/D;IACA,OAAOP,OAAO,CAACG,KAAK;IACpBH,OAAO,CAAC5B,KAAK,GAAG4B,OAAO,CAAC5B,KAAK,IAAI,CAAC,CAAC;IACnC4B,OAAO,CAAC5B,KAAK,CAACoC,UAAU,GAAG,IAAI,CAACC,sBAAsB,CAAC,CAAC;IACxD;IACAT,OAAO,CAAC5B,KAAK,CAACQ,SAAS,GAAGoB,OAAO,CAACI,SAAS;IAE3CJ,OAAO,CAACU,cAAc,GAAG,IAAI,CAACC,sBAAsB,CAAC,CAAC;IACtDX,OAAO,CAACY,qBAAqB,GAAG,MAAM;IAEtC,OAAO,IAAI,CAACC,8BAA8B,CAACb,OAAO,CAAC;EACrD,CAAC;EAGD;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEa,8BAA8B,WAAAA,+BAACb,OAAO,EAAE;IACtC,IAAI,CAACH,MAAM,CAACiB,IAAI,CAAC,yDAAyD,CAAC;IAC3E,IAAI,CAAChD,KAAK,CAACC,SAAS,CAAC,CAAC,CAACJ,QAAQ,GAAG,IAAI,CAACG,KAAK,CAACiD,WAAW,CAACC,aAAa,CACpE,IAAAC,OAAA,CAAAxE,OAAA,EAAc;MAACyE,aAAa,EAAE;IAAM,CAAC,EAAElB,OAAO,CAChD,CAAC;IAED,OAAOR,QAAA,CAAA/C,OAAA,CAAQgD,OAAO,CAAC,CAAC;EAC1B,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACE0B,MAAM,WAAAA,OAAA,EAAe;IAAA,IAAdnB,OAAO,GAAA7C,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAA8C,SAAA,GAAA9C,SAAA,MAAG,CAAC,CAAC;IACjB,IAAI,CAAC6C,OAAO,CAACoB,UAAU,EAAE;MACvB,IAAI,CAACtD,KAAK,CAACC,SAAS,CAAC,CAAC,CAACJ,QAAQ,GAAG,IAAI,CAACG,KAAK,CAACiD,WAAW,CAACM,cAAc,CAACrB,OAAO,CAAC;IAClF;EACF,CAAC;EAID;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEL,6BAA6B,WAAAA,8BAAA,EAAe;IAAA,IAAA2B,MAAA;IAAA,IAAdtB,OAAO,GAAA7C,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAA8C,SAAA,GAAA9C,SAAA,MAAG,CAAC,CAAC;IACxC,IAAI,CAAC0C,MAAM,CAACiB,IAAI,CAAC,kDAAkD,CAAC;IAEpE,IAAI,CAACd,OAAO,CAAC9B,IAAI,EAAE;MACjB,OAAOsB,QAAA,CAAA/C,OAAA,CAAQ8E,MAAM,CAAC,IAAIC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChE;IAEA,IAAMC,IAAI,GAAG;MACXC,UAAU,EAAE,oBAAoB;MAChCC,YAAY,EAAE,IAAI,CAACC,MAAM,CAACD,YAAY;MACtCzD,IAAI,EAAE8B,OAAO,CAAC9B,IAAI;MAClB2D,oBAAoB,EAAE;IACxB,CAAC;IAED,IAAI7B,OAAO,CAACxB,YAAY,EAAE;MACxBiD,IAAI,CAACK,aAAa,GAAG9B,OAAO,CAACxB,YAAY;IAC3C;IAEA,OAAO,IAAI,CAACV,KAAK,CACdiE,OAAO,CAAC;MACPC,MAAM,EAAE,MAAM;MACdC,GAAG,EAAE,IAAI,CAACL,MAAM,CAACM,QAAQ;MACzBT,IAAI,EAAJA,IAAI;MACJU,IAAI,EAAE;QACJC,IAAI,EAAE,IAAI,CAACR,MAAM,CAACS,SAAS;QAC3BC,IAAI,EAAE,IAAI,CAACV,MAAM,CAACW,aAAa;QAC/BC,eAAe,EAAE;MACnB,CAAC;MACDC,wBAAwB,EAAE;IAC5B,CAAC,CAAC,CACD/C,IAAI,CAAC,UAACgD,GAAG,EAAK;MACbpB,MAAI,CAACxD,KAAK,CAACiD,WAAW,CAAC4B,GAAG,CAAC;QAACC,UAAU,EAAEF,GAAG,CAACG;MAAI,CAAC,CAAC;IACpD,CAAC,CAAC,CACDtD,KAAK,CAAC,UAACmD,GAAG,EAAK;MACd,IAAIA,GAAG,CAACI,UAAU,KAAK,GAAG,EAAE;QAC1B,OAAOtD,QAAA,CAAA/C,OAAA,CAAQ8E,MAAM,CAACmB,GAAG,CAAC;MAC5B;MAEA,IAAMK,gBAAgB,GAAGC,sBAAW,CAACC,MAAM,CAACP,GAAG,CAACG,IAAI,CAACjD,KAAK,CAAC;MAE3D,OAAOJ,QAAA,CAAA/C,OAAA,CAAQ8E,MAAM,CAAC,IAAIwB,gBAAgB,CAACL,GAAG,CAACQ,IAAI,IAAIR,GAAG,CAAC,CAAC;IAC9D,CAAC,CAAC;EACN,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;EACES,eAAe,WAAAA,gBAAA,EAAG;IAAA,IAAAC,MAAA;IAChB,IAAI,IAAI,CAACvG,cAAc,EAAE;MACvB,IAAI,CAACC,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;QACpCC,SAAS,EAAE,oBAAoB;QAC/BC,IAAI,EAAE;UAACC,OAAO,EAAE;QAAoC;MACtD,CAAC,CAAC;MACF;IACF;IAEA,IAAI,CAAC1F,KAAK,CACPiE,OAAO,CAAC;MACPC,MAAM,EAAE,MAAM;MACdyB,OAAO,EAAE,cAAc;MACvBC,QAAQ,EAAE,2BAA2B;MACrCjC,IAAI,EAAE;QACJY,SAAS,EAAE,IAAI,CAACT,MAAM,CAACS,SAAS;QAChCsB,KAAK,EAAE,IAAI,CAAC/B,MAAM,CAAC+B;MACrB,CAAC;MACDxB,IAAI,EAAE;QACJC,IAAI,EAAE,IAAI,CAACR,MAAM,CAACS,SAAS;QAC3BC,IAAI,EAAE,IAAI,CAACV,MAAM,CAACW,aAAa;QAC/BC,eAAe,EAAE;MACnB;IACF,CAAC,CAAC,CACD9C,IAAI,CAAC,UAACgD,GAAG,EAAK;MACb,IAAAkB,SAAA,GAAiElB,GAAG,CAACG,IAAI;QAAlEgB,SAAS,GAAAD,SAAA,CAATC,SAAS;QAAEC,gBAAgB,GAAAF,SAAA,CAAhBE,gBAAgB;QAAEC,yBAAyB,GAAAH,SAAA,CAAzBG,yBAAyB;MAC7DX,MAAI,CAACtG,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;QACpCC,SAAS,EAAE,oBAAoB;QAC/BU,QAAQ,EAAE;UACRC,QAAQ,EAAEJ,SAAS;UACnBK,eAAe,EAAEJ,gBAAgB;UACjCK,uBAAuB,EAAEJ;QAC3B;MACF,CAAC,CAAC;MACF;MACAX,MAAI,CAACgB,mBAAmB,CAAC1B,GAAG,CAACG,IAAI,CAAC;IACpC,CAAC,CAAC,CACDtD,KAAK,CAAC,UAACmD,GAAG,EAAK;MACdU,MAAI,CAACtG,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;QACpCC,SAAS,EAAE,oBAAoB;QAC/BC,IAAI,EAAEb,GAAG,CAACG;MACZ,CAAC,CAAC;IACJ,CAAC,CAAC;EACN,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;EACEuB,mBAAmB,WAAAA,oBAAA,EAAe;IAAA,IAAAC,MAAA;IAAA,IAAdrE,OAAO,GAAA7C,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAA8C,SAAA,GAAA9C,SAAA,MAAG,CAAC,CAAC;IAC9B,IAAI,CAAC6C,OAAO,CAACsE,WAAW,EAAE;MACxB,IAAI,CAACxH,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;QACpCC,SAAS,EAAE,sBAAsB;QACjCC,IAAI,EAAE;UAACC,OAAO,EAAE;QAA0B;MAC5C,CAAC,CAAC;MACF;IACF;IAEA,IAAI,IAAI,CAAC3G,cAAc,EAAE;MACvB,IAAI,CAACC,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;QACpCC,SAAS,EAAE,sBAAsB;QACjCC,IAAI,EAAE;UAACC,OAAO,EAAE;QAAoC;MACtD,CAAC,CAAC;MACF;IACF;IAEA,IAAoBe,UAAU,GAA+CvE,OAAO,CAA7EsE,WAAW;MAAAE,iBAAA,GAA2DxE,OAAO,CAApDyE,QAAQ;MAARA,QAAQ,GAAAD,iBAAA,cAAG,CAAC,GAAAA,iBAAA;MAAAE,mBAAA,GAAiC1E,OAAO,CAAtC2E,UAAU;MAAEC,SAAS,GAAAF,mBAAA,cAAG,GAAG,GAAAA,mBAAA;IAEzE,IAAIG,QAAQ,GAAG,CAAC;IAChB,IAAMC,WAAW,GAAGF,SAAS,GAAGH,QAAQ;IAExC,IAAI,CAAC5H,cAAc,GAAGkI,WAAW,CAAC,YAAM;MACtCF,QAAQ,IAAI,CAAC;MAEb,IAAMG,eAAe,GAAGH,QAAQ;MAChCR,MAAI,CAACvG,KAAK,CACPiE,OAAO,CAAC;QACPC,MAAM,EAAE,MAAM;QACdyB,OAAO,EAAE,cAAc;QACvBC,QAAQ,EAAE,uBAAuB;QACjCjC,IAAI,EAAE;UACJC,UAAU,EAAE,8CAA8C;UAC1D4C,WAAW,EAAEC,UAAU;UACvBlC,SAAS,EAAEgC,MAAI,CAACzC,MAAM,CAACS;QACzB,CAAC;QACDF,IAAI,EAAE;UACJC,IAAI,EAAEiC,MAAI,CAACzC,MAAM,CAACS,SAAS;UAC3BC,IAAI,EAAE+B,MAAI,CAACzC,MAAM,CAACW,aAAa;UAC/BC,eAAe,EAAE;QACnB;MACF,CAAC,CAAC,CACD9C,IAAI,CAAC,UAACgD,GAAG,EAAK;QACb,IAAI2B,MAAI,CAACxH,cAAc,KAAK,IAAI,EAAE;QAElCwH,MAAI,CAACvH,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;UACpCC,SAAS,EAAE,sBAAsB;UACjCC,IAAI,EAAEb,GAAG,CAACG;QACZ,CAAC,CAAC;QACFwB,MAAI,CAACY,mBAAmB,CAAC,CAAC;MAC5B,CAAC,CAAC,CACD1F,KAAK,CAAC,UAACmD,GAAG,EAAK;QACd,IAAI2B,MAAI,CAACxH,cAAc,KAAK,IAAI,EAAE;QAElC,IAAImI,eAAe,IAAIF,WAAW,EAAE;UAClCT,MAAI,CAACvH,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;YACpCC,SAAS,EAAE,sBAAsB;YACjCC,IAAI,EAAE;cAACC,OAAO,EAAE;YAAyB;UAC3C,CAAC,CAAC;UACFa,MAAI,CAACY,mBAAmB,CAAC,CAAC;UAC1B;QACF;QACA;QACA;QACA,IAAIvC,GAAG,CAACI,UAAU,KAAK,GAAG,EAAE;UAC1BuB,MAAI,CAACvH,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;YACpCC,SAAS,EAAE,sBAAsB;YACjCC,IAAI,EAAEb,GAAG,CAACG;UACZ,CAAC,CAAC;UACF;QACF;QAEAwB,MAAI,CAACY,mBAAmB,CAAC,CAAC;QAE1BZ,MAAI,CAACvH,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;UACpCC,SAAS,EAAE,sBAAsB;UACjCC,IAAI,EAAEb,GAAG,CAACG;QACZ,CAAC,CAAC;MACJ,CAAC,CAAC;IACN,CAAC,EAAE4B,QAAQ,GAAG,IAAI,CAAC;EACrB,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;EACEQ,mBAAmB,WAAAA,oBAAA,EAAG;IACpB,IAAI,IAAI,CAACpI,cAAc,EAAE;MACvBqI,aAAa,CAAC,IAAI,CAACrI,cAAc,CAAC;MAClC,IAAI,CAACC,YAAY,CAACuG,IAAI,CAAC,aAAa,EAAE;QACpCC,SAAS,EAAE;MACb,CAAC,CAAC;MACF,IAAI,CAACzG,cAAc,GAAG,IAAI;IAC5B;EACF,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEoC,qBAAqB,WAAAA,sBAACf,IAAI,EAAE;IAC1B,OAAO,CAAAA,IAAI,aAAJA,IAAI,uBAAJA,IAAI,CAAEiH,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAIlF,SAAS;EACzC,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEhC,eAAe,WAAAA,gBAACN,QAAQ,EAAE;IACxB,IAAOQ,KAAK,GAAIR,QAAQ,CAAjBQ,KAAK;IAEZ,IAAIA,KAAK,IAAIA,KAAK,CAACyB,KAAK,EAAE;MACxB,IAAMmD,gBAAgB,GAAGC,sBAAW,CAACC,MAAM,CAAC9E,KAAK,CAACyB,KAAK,CAAC;MAExD,MAAM,IAAImD,gBAAgB,CAAC5E,KAAK,CAAC;IACnC;EACF,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEW,SAAS,WAAAA,UAACnB,QAAQ,EAAE;IAClBA,QAAQ,GAAG,IAAAuC,iBAAS,EAACvC,QAAQ,CAAC;IAC9B,IAAI,IAAI,CAACG,KAAK,CAACC,SAAS,CAAC,CAAC,CAACqH,OAAO,IAAI,IAAI,CAACtH,KAAK,CAACC,SAAS,CAAC,CAAC,CAACqH,OAAO,CAACC,YAAY,EAAE;MACjF,IAAAC,eAAA,CAAA7I,OAAA,EAAuBkB,QAAQ,CAACQ,KAAK,EAAE,MAAM,CAAC;MAC9C,IAAI,IAAAoH,eAAO,EAAC,IAAAC,YAAI,EAAC7H,QAAQ,CAACQ,KAAK,CAACC,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE;QACrD,IAAAkH,eAAA,CAAA7I,OAAA,EAAuBkB,QAAQ,CAACQ,KAAK,EAAE,OAAO,CAAC;MACjD,CAAC,MAAM;QACLR,QAAQ,CAACQ,KAAK,CAACC,KAAK,GAAGE,cAAM,CAACmH,MAAM,CAClC,IAAAC,UAAA,CAAAjJ,OAAA,EAAe,IAAA+I,YAAI,EAAC7H,QAAQ,CAACQ,KAAK,CAACC,KAAK,EAAE,YAAY,CAAC,CACzD,CAAC;MACH;MACAT,QAAQ,CAACgI,MAAM,GAAGC,oBAAW,CAACC,SAAS,CAAClI,QAAQ,CAACQ,KAAK,CAAC;MACvD,IAAAmH,eAAA,CAAA7I,OAAA,EAAuBkB,QAAQ,EAAE,OAAO,CAAC;MACzC,IAAI,CAACG,KAAK,CAACC,SAAS,CAAC,CAAC,CAACqH,OAAO,CAACC,YAAY,CAAC,CAAC,CAAC,EAAE,IAAI,EAAEzH,YAAG,CAACkI,MAAM,CAACnI,QAAQ,CAAC,CAAC;IAC7E;EACF,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;EACEgD,sBAAsB,WAAAA,uBAAA,EAAG;IACvB,IAAI,CAACd,MAAM,CAACiB,IAAI,CAAC,+CAA+C,CAAC;;IAEjE;IACA,IAAMiF,gBAAgB,GAAGC,qBAAS,CAACC,SAAS;IAE5C,IAAMzH,YAAY,GAAG5C,MAAM,CACxBsK,KAAK,CAAC,GAAG,EAAE;MAAA,OAAMH,gBAAgB,CAACnK,MAAM,CAACuK,MAAM,CAAC,CAAC,EAAEJ,gBAAgB,CAAC3I,MAAM,GAAG,CAAC,CAAC,CAAC;IAAA,EAAC,CACjFgJ,IAAI,CAAC,EAAE,CAAC;IAEX,IAAMC,aAAa,GAAGhG,iBAAQ,CAACC,MAAM,CAAC9B,YAAY,CAAC,CAAC+B,QAAQ,CAACyF,qBAAS,CAAC;IAEvE,IAAI,CAAClI,KAAK,CAACC,SAAS,CAAC,CAAC,CAACU,cAAc,CAAC6H,OAAO,CAACxK,oBAAoB,EAAE0C,YAAY,CAAC;IAEjF,OAAO6H,aAAa;EACtB,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;EACE5F,sBAAsB,WAAAA,uBAAA,EAAG;IACvB,IAAI,CAACZ,MAAM,CAACiB,IAAI,CAAC,sCAAsC,CAAC;IAExD,IAAMyF,KAAK,GAAGC,aAAI,CAACC,EAAE,CAAC,CAAC;IAEvB,IAAI,CAAC3I,KAAK,CAACC,SAAS,CAAC,CAAC,CAACU,cAAc,CAAC6H,OAAO,CAAC,mBAAmB,EAAEC,KAAK,CAAC;IAEzE,OAAOA,KAAK;EACd,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE1H,oBAAoB,WAAAA,qBAACV,KAAK,EAAE;IAC1B,IAAMuI,YAAY,GAAG,IAAI,CAAC5I,KAAK,CAACC,SAAS,CAAC,CAAC,CAACU,cAAc,CAACC,OAAO,CAAC7C,iBAAiB,CAAC;IAErF,IAAI,CAACiC,KAAK,CAACC,SAAS,CAAC,CAAC,CAACU,cAAc,CAACE,UAAU,CAAC9C,iBAAiB,CAAC;IACnE,IAAI,CAAC6K,YAAY,EAAE;MACjB;IACF;IAEA,IAAI,CAACvI,KAAK,CAACC,KAAK,EAAE;MAChB,MAAM,IAAIoD,KAAK,wBAAAmF,MAAA,CAAwBD,YAAY,sCAAmC,CAAC;IACzF;IAEA,IAAI,CAACvI,KAAK,CAACC,KAAK,CAACoC,UAAU,EAAE;MAC3B,MAAM,IAAIgB,KAAK,wBAAAmF,MAAA,CAAwBD,YAAY,sCAAmC,CAAC;IACzF;IAEA,IAAMH,KAAK,GAAGpI,KAAK,CAACC,KAAK,CAACoC,UAAU;IAEpC,IAAI+F,KAAK,KAAKG,YAAY,EAAE;MAC1B,MAAM,IAAIlF,KAAK,eAAAmF,MAAA,CAAeJ,KAAK,mCAAAI,MAAA,CAAgCD,YAAY,CAAE,CAAC;IACpF;EACF,CAAC;EAAAE,OAAA;AACH,CAAC,OAAAC,0BAAA,CAAApK,OAAA,EAAAd,IAAA,qCAAAF,IAAA,OAAAqL,yBAAA,CAAArK,OAAA,EAAAd,IAAA,qCAAAA,IAAA,OAAAkL,0BAAA,CAAApK,OAAA,EAAAd,IAAA,oCAAAD,KAAA,EAlVEqL,iBAAS,OAAAD,yBAAA,CAAArK,OAAA,EAAAd,IAAA,oCAAAA,IAAA,IAAAA,IAAA,EAkVX,CAAC;AAAC,IAAAqL,QAAA,GAAAC,OAAA,CAAAxK,OAAA,GAEYV,aAAa"}
1
+ {"version":3,"names":["_querystring","_interopRequireDefault","require","_url","_events","_common","_webexCore","_lodash","_uuid","_encBase64url","_cryptoJs","_dec","_dec2","_obj","lodash","OAUTH2_CSRF_TOKEN","OAUTH2_CODE_VERIFIER","Events","exports","qRCodeLogin","Authorization","WebexPlugin","extend","whileInFlight","derived","isAuthenticating","deps","fn","isAuthorizing","session","default","type","ready","namespace","eventEmitter","EventEmitter","pollingTimer","pollingExpirationTimer","pollingId","currentPollingId","initialize","_this","_len","arguments","length","attrs","Array","_key","ret","_apply","prototype","location","url","parse","webex","getWindow","href","_checkForErrors","code","query","state","JSON","base64","decode","codeVerifier","sessionStorage","getItem","removeItem","emailhash","_verifySecurityToken","_cleanUrl","preauthCatalogParams","orgId","_extractOrgIdFromCode","process","nextTick","internal","services","collectPreauthCatalog","catch","_promise","resolve","then","requestAuthorizationCodeGrant","error","logger","warn","initiateLogin","options","undefined","cloneDeep","email","emailHash","CryptoJS","SHA256","toString","csrf_token","_generateSecurityToken","code_challenge","_generateCodeChallenge","code_challenge_method","initiateAuthorizationCodeGrant","info","credentials","buildLoginUrl","_assign","response_type","logout","noRedirect","buildLogoutUrl","_this2","reject","Error","form","grant_type","redirect_uri","config","self_contained_token","code_verifier","request","method","uri","tokenUrl","auth","user","client_id","pass","client_secret","sendImmediately","shouldRefreshAccessToken","res","set","supertoken","body","statusCode","ErrorConstructor","grantErrors","select","_res","_generateQRCodeVerificationUrl","verificationUrl","baseUrl","urlParams","URLSearchParams","URL","search","userCode","get","oauthHelperUrl","newVerificationUrl","searchParams","initQRCodeLogin","_this3","emit","eventType","data","message","service","resource","scope","_res$body","user_code","verification_uri","verification_uri_complete","verificationUriComplete","userData","verificationUri","_startQRCodePolling","_options$interval","_this4","device_code","deviceCode","_options$expires_in","expires_in","expiresIn","interval","setTimeout","cancelQRCodePolling","polling","schedulePolling","withCancelEvent","clearTimeout","split","history","replaceState","_deleteProperty","isEmpty","omit","encode","_stringify","querystring","stringify","format","safeCharacterMap","base64url","_safe_map","times","random","join","codeChallenge","setItem","token","uuid","v4","sessionToken","concat","version","_applyDecoratedDescriptor2","_getOwnPropertyDescriptor","oneFlight","_default"],"sources":["authorization.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint camelcase: [0] */\n\nimport querystring from 'querystring';\nimport url from 'url';\nimport {EventEmitter} from 'events';\n\nimport {base64, oneFlight, whileInFlight} from '@webex/common';\nimport {grantErrors, WebexPlugin} from '@webex/webex-core';\nimport {cloneDeep, isEmpty, omit} from 'lodash';\nimport uuid from 'uuid';\nimport base64url from 'crypto-js/enc-base64url';\nimport CryptoJS from 'crypto-js';\n\n// Necessary to require lodash this way in order to stub\n// methods in the unit test\nconst lodash = require('lodash');\n\nconst OAUTH2_CSRF_TOKEN = 'oauth2-csrf-token';\nconst OAUTH2_CODE_VERIFIER = 'oauth2-code-verifier';\n\n/**\n * Authorization plugin events\n */\nexport const Events = {\n /**\n * QR code login events\n */\n qRCodeLogin: 'qRCodeLogin',\n};\n\n/**\n * Browser support for OAuth2. Automatically parses the URL query for an\n * authorization code\n *\n * Use of this plugin for anything other than the Webex Web Client is strongly\n * discouraged and may be broken at any time\n * @class\n * @name AuthorizationBrowserFirstParty\n * @private\n */\nconst Authorization = WebexPlugin.extend({\n derived: {\n /**\n * Alias of {@link AuthorizationBrowserFirstParty#isAuthorizing}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthenticating: {\n deps: ['isAuthorizing'],\n fn() {\n return this.isAuthorizing;\n },\n },\n },\n\n session: {\n /**\n * Indicates if an Authorization Code exchange is inflight\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthorizing: {\n default: false,\n type: 'boolean',\n },\n ready: {\n default: false,\n type: 'boolean',\n },\n },\n\n namespace: 'Credentials',\n\n /**\n * EventEmitter for authorization events\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {EventEmitter}\n * @public\n */\n eventEmitter: new EventEmitter(),\n\n /**\n * Stores the timer ID for QR code polling\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {?number}\n * @private\n */\n pollingTimer: null,\n /**\n * Stores the expiration timer ID for QR code polling\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {?number}\n * @private\n */\n pollingExpirationTimer: null,\n\n /**\n * Monotonically increasing id to identify the current polling request\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {number}\n * @private\n */\n pollingId: 0,\n\n /**\n * Identifier for the current polling request\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {?number}\n * @private\n */\n currentPollingId: null,\n\n /**\n * Initializer\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Authorization}\n */\n // eslint-disable-next-line complexity\n initialize(...attrs) {\n const ret = Reflect.apply(WebexPlugin.prototype.initialize, this, attrs);\n const location = url.parse(this.webex.getWindow().location.href, true);\n\n this._checkForErrors(location);\n\n const {code} = location.query;\n\n if (!code) {\n this.ready = true;\n\n return ret;\n }\n\n if (location.query.state) {\n location.query.state = JSON.parse(base64.decode(location.query.state));\n } else {\n location.query.state = {};\n }\n\n const codeVerifier = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CODE_VERIFIER);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CODE_VERIFIER);\n\n const {emailhash} = location.query.state;\n\n this._verifySecurityToken(location.query);\n this._cleanUrl(location);\n\n let preauthCatalogParams;\n\n const orgId = this._extractOrgIdFromCode(code);\n\n if (emailhash) {\n preauthCatalogParams = {emailhash};\n } else if (orgId) {\n preauthCatalogParams = {orgId};\n }\n\n // Wait until nextTick in case `credentials` hasn't initialized yet\n process.nextTick(() => {\n this.webex.internal.services\n .collectPreauthCatalog(preauthCatalogParams)\n .catch(() => Promise.resolve())\n .then(() => this.requestAuthorizationCodeGrant({code, codeVerifier}))\n .catch((error) => {\n this.logger.warn('authorization: failed initial authorization code grant request', error);\n })\n .then(() => {\n this.ready = true;\n });\n });\n\n return ret;\n },\n\n /**\n * Kicks off an oauth flow\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateLogin(options = {}) {\n options = cloneDeep(options);\n if (options.email) {\n options.emailHash = CryptoJS.SHA256(options.email).toString();\n }\n delete options.email;\n options.state = options.state || {};\n options.state.csrf_token = this._generateSecurityToken();\n // catalog uses emailhash and redirectCI uses emailHash\n options.state.emailhash = options.emailHash;\n\n options.code_challenge = this._generateCodeChallenge();\n options.code_challenge_method = 'S256';\n\n return this.initiateAuthorizationCodeGrant(options);\n },\n\n @whileInFlight('isAuthorizing')\n /**\n * Kicks off the Implicit Code grant flow. Typically called via\n * {@link AuthorizationBrowserFirstParty#initiateLogin}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateAuthorizationCodeGrant(options) {\n this.logger.info('authorization: initiating authorization code grant flow');\n this.webex.getWindow().location = this.webex.credentials.buildLoginUrl(\n Object.assign({response_type: 'code'}, options)\n );\n\n return Promise.resolve();\n },\n\n /**\n * Called by {@link WebexCore#logout()}. Redirects to the logout page\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {boolean} options.noRedirect if true, does not redirect\n * @returns {Promise}\n */\n logout(options = {}) {\n if (!options.noRedirect) {\n this.webex.getWindow().location = this.webex.credentials.buildLogoutUrl(options);\n }\n },\n\n @whileInFlight('isAuthorizing')\n @oneFlight\n /**\n * Exchanges an authorization code for an access token\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {Object} options.code\n * @returns {Promise}\n */\n requestAuthorizationCodeGrant(options = {}) {\n this.logger.info('credentials: requesting authorization code grant');\n\n if (!options.code) {\n return Promise.reject(new Error('`options.code` is required'));\n }\n\n const form = {\n grant_type: 'authorization_code',\n redirect_uri: this.config.redirect_uri,\n code: options.code,\n self_contained_token: true,\n };\n\n if (options.codeVerifier) {\n form.code_verifier = options.codeVerifier;\n }\n\n return this.webex\n .request({\n method: 'POST',\n uri: this.config.tokenUrl,\n form,\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true,\n },\n shouldRefreshAccessToken: false,\n })\n .then((res) => {\n this.webex.credentials.set({supertoken: res.body});\n })\n .catch((res) => {\n if (res.statusCode !== 400) {\n return Promise.reject(res);\n }\n\n const ErrorConstructor = grantErrors.select(res.body.error);\n\n return Promise.reject(new ErrorConstructor(res._res || res));\n });\n },\n\n /**\n * Generate a QR code URL to launch the Webex app when scanning with the camera\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {String} verificationUrl\n * @returns {String}\n */\n _generateQRCodeVerificationUrl(verificationUrl) {\n const baseUrl = 'https://web.webex.com/deviceAuth';\n const urlParams = new URLSearchParams(new URL(verificationUrl).search);\n const userCode = urlParams.get('userCode');\n\n if (userCode) {\n const {services} = this.webex.internal;\n const oauthHelperUrl = services.get('oauth-helper');\n const newVerificationUrl = new URL(baseUrl);\n newVerificationUrl.searchParams.set('usercode', userCode);\n newVerificationUrl.searchParams.set('oauthhelper', oauthHelperUrl);\n return newVerificationUrl.toString();\n } else {\n return verificationUrl;\n }\n },\n\n /**\n * Get an OAuth Login URL for QRCode. Generate QR code based on the returned URL.\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @emits #qRCodeLogin\n */\n initQRCodeLogin() {\n if (this.pollingTimer) {\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'getUserCodeFailure',\n data: {message: 'There is already a polling request'},\n });\n return;\n }\n\n this.webex\n .request({\n method: 'POST',\n service: 'oauth-helper',\n resource: '/actions/device/authorize',\n form: {\n client_id: this.config.client_id,\n scope: this.config.scope,\n },\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true,\n },\n })\n .then((res) => {\n const {user_code, verification_uri, verification_uri_complete} = res.body;\n const verificationUriComplete = this._generateQRCodeVerificationUrl(verification_uri_complete);\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'getUserCodeSuccess',\n userData: {\n userCode: user_code,\n verificationUri: verification_uri,\n verificationUriComplete,\n },\n });\n // if device authorization success, then start to poll server to check whether the user has completed authorization\n this._startQRCodePolling(res.body);\n })\n .catch((res) => {\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'getUserCodeFailure',\n data: res.body,\n });\n });\n },\n\n /**\n * Polling the server to check whether the user has completed authorization\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @emits #qRCodeLogin\n */\n _startQRCodePolling(options = {}) {\n if (!options.device_code) {\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'authorizationFailure',\n data: {message: 'A deviceCode is required'},\n });\n return;\n }\n\n if (this.pollingTimer) {\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'authorizationFailure',\n data: {message: 'There is already a polling request'},\n });\n return;\n }\n\n const {device_code: deviceCode, expires_in: expiresIn = 300} = options;\n let interval = options.interval ?? 2;\n\n this.pollingExpirationTimer = setTimeout(() => {\n this.cancelQRCodePolling(false);\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'authorizationFailure',\n data: {message: 'Authorization timed out'},\n });\n }, expiresIn * 1000);\n\n const polling = () => {\n this.pollingId += 1;\n this.currentPollingId = this.pollingId;\n\n this.webex\n .request({\n method: 'POST',\n service: 'oauth-helper',\n resource: '/actions/device/token',\n form: {\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: this.config.client_id,\n },\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true,\n },\n })\n .then((res) => {\n // if the pollingId has changed, it means that the polling request has been canceled\n if (this.currentPollingId !== this.pollingId) return;\n\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'authorizationSuccess',\n data: res.body,\n });\n this.cancelQRCodePolling();\n })\n .catch((res) => {\n // if the pollingId has changed, it means that the polling request has been canceled\n if (this.currentPollingId !== this.pollingId) return;\n\n // When server sends 400 status code with message 'slow_down', it means that last request happened too soon.\n // So, skip one interval and then poll again.\n if (res.statusCode === 400 && res.body.message === 'slow_down') {\n schedulePolling(interval * 2);\n return;\n }\n\n // if the statusCode is 428 which means that the authorization request is still pending\n // as the end user hasn't yet completed the user-interaction steps. So keep polling.\n if (res.statusCode === 428) {\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'authorizationPending',\n data: res.body,\n });\n schedulePolling(interval);\n return;\n }\n\n this.cancelQRCodePolling();\n\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'authorizationFailure',\n data: res.body,\n });\n });\n };\n\n const schedulePolling = (interval) =>\n (this.pollingTimer = setTimeout(polling, interval * 1000));\n\n schedulePolling(interval);\n },\n\n /**\n * cancel polling request\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @returns {void}\n */\n cancelQRCodePolling(withCancelEvent = true) {\n if (this.pollingTimer && withCancelEvent) {\n this.eventEmitter.emit(Events.qRCodeLogin, {\n eventType: 'pollingCanceled',\n });\n }\n\n this.currentPollingId = null;\n\n clearTimeout(this.pollingExpirationTimer);\n this.pollingExpirationTimer = null;\n clearTimeout(this.pollingTimer);\n this.pollingTimer = null;\n },\n\n /**\n * Extracts the orgId from the returned code from idbroker\n * Description of how to parse the code can be found here:\n * https://wiki.cisco.com/display/IDENTITY/Federated+Token+Validation\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {String} code\n * @private\n * @returns {String}\n */\n _extractOrgIdFromCode(code) {\n return code?.split('_')[2] || undefined;\n },\n\n /**\n * Checks if the result of the login redirect contains an error string\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _checkForErrors(location) {\n const {query} = location;\n\n if (query && query.error) {\n const ErrorConstructor = grantErrors.select(query.error);\n\n throw new ErrorConstructor(query);\n }\n },\n\n /**\n * Removes no-longer needed values from the url (access token, csrf token, etc)\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _cleanUrl(location) {\n location = cloneDeep(location);\n if (this.webex.getWindow().history && this.webex.getWindow().history.replaceState) {\n Reflect.deleteProperty(location.query, 'code');\n if (isEmpty(omit(location.query.state, 'csrf_token'))) {\n Reflect.deleteProperty(location.query, 'state');\n } else {\n location.query.state = base64.encode(\n JSON.stringify(omit(location.query.state, 'csrf_token'))\n );\n }\n location.search = querystring.stringify(location.query);\n Reflect.deleteProperty(location, 'query');\n this.webex.getWindow().history.replaceState({}, null, url.format(location));\n }\n },\n\n /**\n * Generates PKCE code verifier and code challenge and sets the the code verifier in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {string}\n */\n _generateCodeChallenge() {\n this.logger.info('authorization: generating PKCE code challenge');\n\n // eslint-disable-next-line no-underscore-dangle\n const safeCharacterMap = base64url._safe_map;\n\n const codeVerifier = lodash\n .times(128, () => safeCharacterMap[lodash.random(0, safeCharacterMap.length - 1)])\n .join('');\n\n const codeChallenge = CryptoJS.SHA256(codeVerifier).toString(base64url);\n\n this.webex.getWindow().sessionStorage.setItem(OAUTH2_CODE_VERIFIER, codeVerifier);\n\n return codeChallenge;\n },\n\n /**\n * Generates a CSRF token and sticks in in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Promise}\n */\n _generateSecurityToken() {\n this.logger.info('authorization: generating csrf token');\n\n const token = uuid.v4();\n\n this.webex.getWindow().sessionStorage.setItem('oauth2-csrf-token', token);\n\n return token;\n },\n\n /**\n * Checks if the CSRF token in sessionStorage is the same as the one returned\n * in the url.\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} query\n * @private\n * @returns {Promise}\n */\n _verifySecurityToken(query) {\n const sessionToken = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CSRF_TOKEN);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CSRF_TOKEN);\n if (!sessionToken) {\n return;\n }\n\n if (!query.state) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n if (!query.state.csrf_token) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n const token = query.state.csrf_token;\n\n if (token !== sessionToken) {\n throw new Error(`CSRF token ${token} does not match stored token ${sessionToken}`);\n }\n },\n});\n\nexport default Authorization;\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,10 +1,19 @@
1
1
  "use strict";
2
2
 
3
+ var _typeof = require("@babel/runtime-corejs2/helpers/typeof");
4
+ var _WeakMap = require("@babel/runtime-corejs2/core-js/weak-map");
3
5
  var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
6
+ var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");
4
7
  var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
5
8
  _Object$defineProperty(exports, "__esModule", {
6
9
  value: true
7
10
  });
11
+ _Object$defineProperty(exports, "Events", {
12
+ enumerable: true,
13
+ get: function get() {
14
+ return _authorization.Events;
15
+ }
16
+ });
8
17
  _Object$defineProperty(exports, "config", {
9
18
  enumerable: true,
10
19
  get: function get() {
@@ -18,8 +27,10 @@ _Object$defineProperty(exports, "default", {
18
27
  }
19
28
  });
20
29
  var _webexCore = require("@webex/webex-core");
21
- var _authorization = _interopRequireDefault(require("./authorization"));
30
+ var _authorization = _interopRequireWildcard(require("./authorization"));
22
31
  var _config = _interopRequireDefault(require("./config"));
32
+ function _getRequireWildcardCache(e) { if ("function" != typeof _WeakMap) return null; var r = new _WeakMap(), t = new _WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
33
+ 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; }
23
34
  /*!
24
35
  * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
25
36
  */
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"names":["_webexCore","require","_authorization","_interopRequireDefault","_config","proxies","registerPlugin","Authorization","config"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerPlugin} from '@webex/webex-core';\n\nimport Authorization from './authorization';\nimport config from './config';\n\nconst proxies = ['isAuthorizing', 'isAuthenticating'];\n\nregisterPlugin('authorization', Authorization, {\n config,\n proxies,\n});\n\nexport {default} from './authorization';\nexport {default as config} from './config';\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAIA,IAAAA,UAAA,GAAAC,OAAA;AAEA,IAAAC,cAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAD,sBAAA,CAAAF,OAAA;AAPA;AACA;AACA;;AAOA,IAAMI,OAAO,GAAG,CAAC,eAAe,EAAE,kBAAkB,CAAC;AAErD,IAAAC,yBAAc,EAAC,eAAe,EAAEC,sBAAa,EAAE;EAC7CC,MAAM,EAANA,eAAM;EACNH,OAAO,EAAPA;AACF,CAAC,CAAC"}
1
+ {"version":3,"names":["_webexCore","require","_authorization","_interopRequireWildcard","_config","_interopRequireDefault","_getRequireWildcardCache","e","_WeakMap","r","t","__esModule","_typeof","default","has","get","n","__proto__","a","_Object$defineProperty","_Object$getOwnPropertyDescriptor","u","Object","prototype","hasOwnProperty","call","i","set","proxies","registerPlugin","Authorization","config"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerPlugin} from '@webex/webex-core';\n\nimport Authorization from './authorization';\nimport config from './config';\n\nconst proxies = ['isAuthorizing', 'isAuthenticating'];\n\nregisterPlugin('authorization', Authorization, {\n config,\n proxies,\n});\n\nexport {default, Events} from './authorization';\nexport {default as config} from './config';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAAA,UAAA,GAAAC,OAAA;AAEA,IAAAC,cAAA,GAAAC,uBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAC,sBAAA,CAAAJ,OAAA;AAA8B,SAAAK,yBAAAC,CAAA,6BAAAC,QAAA,mBAAAC,CAAA,OAAAD,QAAA,IAAAE,CAAA,OAAAF,QAAA,YAAAF,wBAAA,YAAAA,yBAAAC,CAAA,WAAAA,CAAA,GAAAG,CAAA,GAAAD,CAAA,KAAAF,CAAA;AAAA,SAAAJ,wBAAAI,CAAA,EAAAE,CAAA,SAAAA,CAAA,IAAAF,CAAA,IAAAA,CAAA,CAAAI,UAAA,SAAAJ,CAAA,eAAAA,CAAA,gBAAAK,OAAA,CAAAL,CAAA,0BAAAA,CAAA,WAAAM,OAAA,EAAAN,CAAA,QAAAG,CAAA,GAAAJ,wBAAA,CAAAG,CAAA,OAAAC,CAAA,IAAAA,CAAA,CAAAI,GAAA,CAAAP,CAAA,UAAAG,CAAA,CAAAK,GAAA,CAAAR,CAAA,OAAAS,CAAA,KAAAC,SAAA,UAAAC,CAAA,GAAAC,sBAAA,IAAAC,gCAAA,WAAAC,CAAA,IAAAd,CAAA,oBAAAc,CAAA,IAAAC,MAAA,CAAAC,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAlB,CAAA,EAAAc,CAAA,SAAAK,CAAA,GAAAR,CAAA,GAAAE,gCAAA,CAAAb,CAAA,EAAAc,CAAA,UAAAK,CAAA,KAAAA,CAAA,CAAAX,GAAA,IAAAW,CAAA,CAAAC,GAAA,IAAAR,sBAAA,CAAAH,CAAA,EAAAK,CAAA,EAAAK,CAAA,IAAAV,CAAA,CAAAK,CAAA,IAAAd,CAAA,CAAAc,CAAA,YAAAL,CAAA,CAAAH,OAAA,GAAAN,CAAA,EAAAG,CAAA,IAAAA,CAAA,CAAAiB,GAAA,CAAApB,CAAA,EAAAS,CAAA,GAAAA,CAAA;AAP9B;AACA;AACA;;AAOA,IAAMY,OAAO,GAAG,CAAC,eAAe,EAAE,kBAAkB,CAAC;AAErD,IAAAC,yBAAc,EAAC,eAAe,EAAEC,sBAAa,EAAE;EAC7CC,MAAM,EAANA,eAAM;EACNH,OAAO,EAAPA;AACF,CAAC,CAAC"}
package/package.json CHANGED
@@ -56,5 +56,5 @@
56
56
  "test:style": "eslint ./src/**/*.*",
57
57
  "test:unit": "webex-legacy-tools test --unit --runner jest"
58
58
  },
59
- "version": "3.6.0-next.5"
59
+ "version": "3.6.0-next.7"
60
60
  }
@@ -22,6 +22,16 @@ const lodash = require('lodash');
22
22
  const OAUTH2_CSRF_TOKEN = 'oauth2-csrf-token';
23
23
  const OAUTH2_CODE_VERIFIER = 'oauth2-code-verifier';
24
24
 
25
+ /**
26
+ * Authorization plugin events
27
+ */
28
+ export const Events = {
29
+ /**
30
+ * QR code login events
31
+ */
32
+ qRCodeLogin: 'qRCodeLogin',
33
+ };
34
+
25
35
  /**
26
36
  * Browser support for OAuth2. Automatically parses the URL query for an
27
37
  * authorization code
@@ -67,17 +77,49 @@ const Authorization = WebexPlugin.extend({
67
77
 
68
78
  namespace: 'Credentials',
69
79
 
80
+ /**
81
+ * EventEmitter for authorization events
82
+ * @instance
83
+ * @memberof AuthorizationBrowserFirstParty
84
+ * @type {EventEmitter}
85
+ * @public
86
+ */
87
+ eventEmitter: new EventEmitter(),
70
88
 
71
89
  /**
72
- * Stores the interval ID for QR code polling
90
+ * Stores the timer ID for QR code polling
73
91
  * @instance
74
92
  * @memberof AuthorizationBrowserFirstParty
75
93
  * @type {?number}
76
94
  * @private
77
95
  */
78
- pollingRequest: null,
96
+ pollingTimer: null,
97
+ /**
98
+ * Stores the expiration timer ID for QR code polling
99
+ * @instance
100
+ * @memberof AuthorizationBrowserFirstParty
101
+ * @type {?number}
102
+ * @private
103
+ */
104
+ pollingExpirationTimer: null,
79
105
 
80
- eventEmitter: new EventEmitter(),
106
+ /**
107
+ * Monotonically increasing id to identify the current polling request
108
+ * @instance
109
+ * @memberof AuthorizationBrowserFirstParty
110
+ * @type {number}
111
+ * @private
112
+ */
113
+ pollingId: 0,
114
+
115
+ /**
116
+ * Identifier for the current polling request
117
+ * @instance
118
+ * @memberof AuthorizationBrowserFirstParty
119
+ * @type {?number}
120
+ * @private
121
+ */
122
+ currentPollingId: null,
81
123
 
82
124
  /**
83
125
  * Initializer
@@ -253,6 +295,30 @@ const Authorization = WebexPlugin.extend({
253
295
  });
254
296
  },
255
297
 
298
+ /**
299
+ * Generate a QR code URL to launch the Webex app when scanning with the camera
300
+ * @instance
301
+ * @memberof AuthorizationBrowserFirstParty
302
+ * @param {String} verificationUrl
303
+ * @returns {String}
304
+ */
305
+ _generateQRCodeVerificationUrl(verificationUrl) {
306
+ const baseUrl = 'https://web.webex.com/deviceAuth';
307
+ const urlParams = new URLSearchParams(new URL(verificationUrl).search);
308
+ const userCode = urlParams.get('userCode');
309
+
310
+ if (userCode) {
311
+ const {services} = this.webex.internal;
312
+ const oauthHelperUrl = services.get('oauth-helper');
313
+ const newVerificationUrl = new URL(baseUrl);
314
+ newVerificationUrl.searchParams.set('usercode', userCode);
315
+ newVerificationUrl.searchParams.set('oauthhelper', oauthHelperUrl);
316
+ return newVerificationUrl.toString();
317
+ } else {
318
+ return verificationUrl;
319
+ }
320
+ },
321
+
256
322
  /**
257
323
  * Get an OAuth Login URL for QRCode. Generate QR code based on the returned URL.
258
324
  * @instance
@@ -260,8 +326,8 @@ const Authorization = WebexPlugin.extend({
260
326
  * @emits #qRCodeLogin
261
327
  */
262
328
  initQRCodeLogin() {
263
- if (this.pollingRequest) {
264
- this.eventEmitter.emit('qRCodeLogin', {
329
+ if (this.pollingTimer) {
330
+ this.eventEmitter.emit(Events.qRCodeLogin, {
265
331
  eventType: 'getUserCodeFailure',
266
332
  data: {message: 'There is already a polling request'},
267
333
  });
@@ -285,19 +351,20 @@ const Authorization = WebexPlugin.extend({
285
351
  })
286
352
  .then((res) => {
287
353
  const {user_code, verification_uri, verification_uri_complete} = res.body;
288
- this.eventEmitter.emit('qRCodeLogin', {
354
+ const verificationUriComplete = this._generateQRCodeVerificationUrl(verification_uri_complete);
355
+ this.eventEmitter.emit(Events.qRCodeLogin, {
289
356
  eventType: 'getUserCodeSuccess',
290
357
  userData: {
291
358
  userCode: user_code,
292
359
  verificationUri: verification_uri,
293
- verificationUriComplete: verification_uri_complete,
294
- }
360
+ verificationUriComplete,
361
+ },
295
362
  });
296
363
  // if device authorization success, then start to poll server to check whether the user has completed authorization
297
364
  this._startQRCodePolling(res.body);
298
365
  })
299
366
  .catch((res) => {
300
- this.eventEmitter.emit('qRCodeLogin', {
367
+ this.eventEmitter.emit(Events.qRCodeLogin, {
301
368
  eventType: 'getUserCodeFailure',
302
369
  data: res.body,
303
370
  });
@@ -313,30 +380,36 @@ const Authorization = WebexPlugin.extend({
313
380
  */
314
381
  _startQRCodePolling(options = {}) {
315
382
  if (!options.device_code) {
316
- this.eventEmitter.emit('qRCodeLogin', {
383
+ this.eventEmitter.emit(Events.qRCodeLogin, {
317
384
  eventType: 'authorizationFailure',
318
385
  data: {message: 'A deviceCode is required'},
319
386
  });
320
387
  return;
321
388
  }
322
389
 
323
- if (this.pollingRequest) {
324
- this.eventEmitter.emit('qRCodeLogin', {
390
+ if (this.pollingTimer) {
391
+ this.eventEmitter.emit(Events.qRCodeLogin, {
325
392
  eventType: 'authorizationFailure',
326
393
  data: {message: 'There is already a polling request'},
327
394
  });
328
395
  return;
329
396
  }
330
397
 
331
- const {device_code: deviceCode, interval = 2, expires_in: expiresIn = 300} = options;
398
+ const {device_code: deviceCode, expires_in: expiresIn = 300} = options;
399
+ let interval = options.interval ?? 2;
332
400
 
333
- let attempts = 0;
334
- const maxAttempts = expiresIn / interval;
401
+ this.pollingExpirationTimer = setTimeout(() => {
402
+ this.cancelQRCodePolling(false);
403
+ this.eventEmitter.emit(Events.qRCodeLogin, {
404
+ eventType: 'authorizationFailure',
405
+ data: {message: 'Authorization timed out'},
406
+ });
407
+ }, expiresIn * 1000);
335
408
 
336
- this.pollingRequest = setInterval(() => {
337
- attempts += 1;
409
+ const polling = () => {
410
+ this.pollingId += 1;
411
+ this.currentPollingId = this.pollingId;
338
412
 
339
- const currentAttempts = attempts;
340
413
  this.webex
341
414
  .request({
342
415
  method: 'POST',
@@ -354,43 +427,50 @@ const Authorization = WebexPlugin.extend({
354
427
  },
355
428
  })
356
429
  .then((res) => {
357
- if (this.pollingRequest === null) return;
430
+ // if the pollingId has changed, it means that the polling request has been canceled
431
+ if (this.currentPollingId !== this.pollingId) return;
358
432
 
359
- this.eventEmitter.emit('qRCodeLogin', {
433
+ this.eventEmitter.emit(Events.qRCodeLogin, {
360
434
  eventType: 'authorizationSuccess',
361
435
  data: res.body,
362
436
  });
363
437
  this.cancelQRCodePolling();
364
438
  })
365
439
  .catch((res) => {
366
- if (this.pollingRequest === null) return;
440
+ // if the pollingId has changed, it means that the polling request has been canceled
441
+ if (this.currentPollingId !== this.pollingId) return;
367
442
 
368
- if (currentAttempts >= maxAttempts) {
369
- this.eventEmitter.emit('qRCodeLogin', {
370
- eventType: 'authorizationFailure',
371
- data: {message: 'Authorization timed out'}
372
- });
373
- this.cancelQRCodePolling();
443
+ // When server sends 400 status code with message 'slow_down', it means that last request happened too soon.
444
+ // So, skip one interval and then poll again.
445
+ if (res.statusCode === 400 && res.body.message === 'slow_down') {
446
+ schedulePolling(interval * 2);
374
447
  return;
375
448
  }
449
+
376
450
  // if the statusCode is 428 which means that the authorization request is still pending
377
451
  // as the end user hasn't yet completed the user-interaction steps. So keep polling.
378
452
  if (res.statusCode === 428) {
379
- this.eventEmitter.emit('qRCodeLogin', {
453
+ this.eventEmitter.emit(Events.qRCodeLogin, {
380
454
  eventType: 'authorizationPending',
381
- data: res.body
455
+ data: res.body,
382
456
  });
457
+ schedulePolling(interval);
383
458
  return;
384
459
  }
385
460
 
386
461
  this.cancelQRCodePolling();
387
462
 
388
- this.eventEmitter.emit('qRCodeLogin', {
463
+ this.eventEmitter.emit(Events.qRCodeLogin, {
389
464
  eventType: 'authorizationFailure',
390
- data: res.body
465
+ data: res.body,
391
466
  });
392
467
  });
393
- }, interval * 1000);
468
+ };
469
+
470
+ const schedulePolling = (interval) =>
471
+ (this.pollingTimer = setTimeout(polling, interval * 1000));
472
+
473
+ schedulePolling(interval);
394
474
  },
395
475
 
396
476
  /**
@@ -399,14 +479,19 @@ const Authorization = WebexPlugin.extend({
399
479
  * @memberof AuthorizationBrowserFirstParty
400
480
  * @returns {void}
401
481
  */
402
- cancelQRCodePolling() {
403
- if (this.pollingRequest) {
404
- clearInterval(this.pollingRequest);
405
- this.eventEmitter.emit('qRCodeLogin', {
482
+ cancelQRCodePolling(withCancelEvent = true) {
483
+ if (this.pollingTimer && withCancelEvent) {
484
+ this.eventEmitter.emit(Events.qRCodeLogin, {
406
485
  eventType: 'pollingCanceled',
407
486
  });
408
- this.pollingRequest = null;
409
487
  }
488
+
489
+ this.currentPollingId = null;
490
+
491
+ clearTimeout(this.pollingExpirationTimer);
492
+ this.pollingExpirationTimer = null;
493
+ clearTimeout(this.pollingTimer);
494
+ this.pollingTimer = null;
410
495
  },
411
496
 
412
497
  /**
package/src/index.js CHANGED
@@ -14,5 +14,5 @@ registerPlugin('authorization', Authorization, {
14
14
  proxies,
15
15
  });
16
16
 
17
- export {default} from './authorization';
17
+ export {default, Events} from './authorization';
18
18
  export {default as config} from './config';
@@ -18,7 +18,6 @@ import Authorization from '@webex/plugin-authorization-browser-first-party';
18
18
  // Necessary to require lodash this way in order to stub the method
19
19
  const lodash = require('lodash');
20
20
 
21
-
22
21
  describe('plugin-authorization-browser-first-party', () => {
23
22
  describe('Authorization', () => {
24
23
  function makeWebex(
@@ -187,14 +186,16 @@ describe('plugin-authorization-browser-first-party', () => {
187
186
  const webex = makeWebex(
188
187
  `http://example.com/?code=${code}&state=${base64.encode(
189
188
  JSON.stringify({emailhash: 'someemailhash'})
190
- )}`,
189
+ )}`
191
190
  );
192
191
 
193
192
  const requestAuthorizationCodeGrantStub = sinon.stub(
194
193
  Authorization.prototype,
195
194
  'requestAuthorizationCodeGrant'
196
195
  );
197
- const collectPreauthCatalogStub = sinon.stub(Services.prototype, 'collectPreauthCatalog').resolves();
196
+ const collectPreauthCatalogStub = sinon
197
+ .stub(Services.prototype, 'collectPreauthCatalog')
198
+ .resolves();
198
199
 
199
200
  await webex.authorization.when('change:ready');
200
201
 
@@ -206,9 +207,7 @@ describe('plugin-authorization-browser-first-party', () => {
206
207
 
207
208
  it('collects the preauth catalog no emailhash is present in the state', async () => {
208
209
  const code = 'authcode_clusterid_theOrgId';
209
- const webex = makeWebex(
210
- `http://example.com/?code=${code}`
211
- );
210
+ const webex = makeWebex(`http://example.com/?code=${code}`);
212
211
 
213
212
  const requestAuthorizationCodeGrantStub = sinon.stub(
214
213
  Authorization.prototype,
@@ -271,12 +270,13 @@ describe('plugin-authorization-browser-first-party', () => {
271
270
  it('throws a grant error', () => {
272
271
  let err = null;
273
272
  try {
274
- makeWebex('http://127.0.0.1:8000/?error=invalid_scope&error_description=The%20requested%20scope%20is%20invalid.');
275
- }
276
- catch (e) {
273
+ makeWebex(
274
+ 'http://127.0.0.1:8000/?error=invalid_scope&error_description=The%20requested%20scope%20is%20invalid.'
275
+ );
276
+ } catch (e) {
277
277
  err = e;
278
278
  }
279
- expect(err?.message).toBe('Cannot convert object to primitive value')
279
+ expect(err?.message).toBe('Cannot convert object to primitive value');
280
280
  });
281
281
  });
282
282
 
@@ -443,17 +443,49 @@ describe('plugin-authorization-browser-first-party', () => {
443
443
  });
444
444
  });
445
445
 
446
+ describe('#_generateQRCodeVerificationUrl()', () => {
447
+ it('should generate a QR code URL when a userCode is present', () => {
448
+ const verificationUrl = 'https://example.com/verify?userCode=123456';
449
+ const oauthHelperUrl = 'https://oauth-helper-a.wbx2.com/helperservice/v1';
450
+ const expectedUrl = 'https://web.webex.com/deviceAuth?usercode=123456&oauthhelper=https%3A%2F%2Foauth-helper-a.wbx2.com%2Fhelperservice%2Fv1';
451
+
452
+ const webex = makeWebex('http://example.com');
453
+
454
+ const oauthHelperSpy = sinon.stub(webex.internal.services, 'get').returns(oauthHelperUrl);
455
+ const result = webex.authorization._generateQRCodeVerificationUrl(verificationUrl);
456
+
457
+ assert.calledOnce(oauthHelperSpy);
458
+ assert.calledWithExactly(oauthHelperSpy, 'oauth-helper');
459
+ assert.equal(result, expectedUrl);
460
+
461
+ oauthHelperSpy.restore();
462
+ });
463
+
464
+ it('should return the original verificationUrl when userCode is not present', () => {
465
+ const verificationUrl = 'https://example.com/verify';
466
+ const webex = makeWebex('http://example.com');
467
+
468
+ const oauthHelperSpy = sinon.stub(webex.internal.services, 'get');
469
+ const result = webex.authorization._generateQRCodeVerificationUrl(verificationUrl);
470
+
471
+ assert.notCalled(oauthHelperSpy);
472
+ assert.equal(result, verificationUrl);
473
+
474
+ oauthHelperSpy.restore();
475
+ });
476
+ });
477
+
446
478
  describe('#initQRCodeLogin()', () => {
447
479
  it('should prevent concurrent request if there is already a polling request', async () => {
448
480
  const webex = makeWebex('http://example.com');
449
-
450
- webex.authorization.pollingRequest = 1;
481
+
482
+ webex.authorization.pollingTimer = 1;
451
483
  const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
452
484
  webex.authorization.initQRCodeLogin();
453
-
485
+
454
486
  assert.calledOnce(emitSpy);
455
- assert.equal(emitSpy.getCall(0).args[1].eventType, 'getUserCodeFailure');
456
- webex.authorization.pollingRequest = null;
487
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'getUserCodeFailure');
488
+ webex.authorization.pollingTimer = null;
457
489
  });
458
490
 
459
491
  it('should send correct request parameters to the API', async () => {
@@ -461,31 +493,33 @@ describe('plugin-authorization-browser-first-party', () => {
461
493
  const testClientId = 'test-client-id';
462
494
  const testScope = 'test-scope';
463
495
  const sampleData = {
464
- device_code: "test123",
496
+ device_code: 'test123',
465
497
  expires_in: 300,
466
- user_code: "421175",
467
- verification_uri: "http://example.com",
468
- verification_uri_complete: "http://example.com",
469
- interval: 2
498
+ user_code: '421175',
499
+ verification_uri: 'http://example.com',
500
+ verification_uri_complete: 'http://example.com',
501
+ interval: 2,
470
502
  };
471
503
 
472
504
  const webex = makeWebex('http://example.com', undefined, undefined, {
473
505
  credentials: {
474
506
  client_id: testClientId,
475
507
  scope: testScope,
476
- }
508
+ },
477
509
  });
478
510
  webex.request.onFirstCall().resolves({statusCode: 200, body: sampleData});
479
511
  sinon.spy(webex.authorization, '_startQRCodePolling');
512
+ sinon.spy(webex.authorization, '_generateQRCodeVerificationUrl');
480
513
  const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
481
514
 
482
515
  webex.authorization.initQRCodeLogin();
483
516
  clock.tick(2000);
484
- await clock.runAllAsync()
517
+ await clock.runAllAsync();
485
518
 
486
519
  assert.calledTwice(webex.request);
487
520
  assert.calledOnce(webex.authorization._startQRCodePolling);
488
- assert.equal(emitSpy.getCall(0).args[1].eventType, 'getUserCodeSuccess');
521
+ assert.calledOnce(webex.authorization._generateQRCodeVerificationUrl);
522
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'getUserCodeSuccess');
489
523
 
490
524
  const request = webex.request.getCall(0);
491
525
 
@@ -498,18 +532,18 @@ describe('plugin-authorization-browser-first-party', () => {
498
532
  const clock = sinon.useFakeTimers();
499
533
  const webex = makeWebex('http://example.com');
500
534
  const sampleData = {
501
- device_code: "test123",
535
+ device_code: 'test123',
502
536
  expires_in: 300,
503
- user_code: "421175",
504
- verification_uri: "http://example.com",
505
- verification_uri_complete: "http://example.com",
506
- interval: 2
537
+ user_code: '421175',
538
+ verification_uri: 'http://example.com',
539
+ verification_uri_complete: 'http://example.com',
540
+ interval: 2,
507
541
  };
508
542
  webex.request.resolves().resolves({statusCode: 200, body: sampleData});
509
543
 
510
544
  webex.authorization.initQRCodeLogin();
511
545
  clock.tick(2000);
512
- await clock.runAllAsync()
546
+ await clock.runAllAsync();
513
547
 
514
548
  const request = webex.request.getCall(0);
515
549
  assert.equal(request.args[0].method, 'POST');
@@ -517,7 +551,7 @@ describe('plugin-authorization-browser-first-party', () => {
517
551
  assert.equal(request.args[0].resource, '/actions/device/authorize');
518
552
  clock.restore();
519
553
  });
520
-
554
+
521
555
  it('should emit getUserCodeFailure event', async () => {
522
556
  const clock = sinon.useFakeTimers();
523
557
  const webex = makeWebex('http://example.com');
@@ -526,7 +560,7 @@ describe('plugin-authorization-browser-first-party', () => {
526
560
 
527
561
  webex.authorization.initQRCodeLogin();
528
562
 
529
- await clock.runAllAsync()
563
+ await clock.runAllAsync();
530
564
 
531
565
  assert.calledOnce(emitSpy);
532
566
  assert.equal(emitSpy.getCall(0).args[1].eventType, 'getUserCodeFailure');
@@ -537,15 +571,15 @@ describe('plugin-authorization-browser-first-party', () => {
537
571
  describe('#_startQRCodePolling()', () => {
538
572
  it('requires a deviceCode', () => {
539
573
  const webex = makeWebex('http://example.com');
540
-
574
+
541
575
  const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
542
576
 
543
577
  webex.authorization._startQRCodePolling({});
544
578
 
545
579
  assert.calledOnce(emitSpy);
546
- assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationFailure');
580
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationFailure');
547
581
  });
548
-
582
+
549
583
  it('should send correct request parameters to the API', async () => {
550
584
  const clock = sinon.useFakeTimers();
551
585
  const testClientId = 'test-client-id';
@@ -560,7 +594,7 @@ describe('plugin-authorization-browser-first-party', () => {
560
594
  const webex = makeWebex('http://example.com', undefined, undefined, {
561
595
  credentials: {
562
596
  client_id: testClientId,
563
- }
597
+ },
564
598
  });
565
599
 
566
600
  webex.request.onFirstCall().resolves({statusCode: 200, body: {access_token: 'token'}});
@@ -569,7 +603,7 @@ describe('plugin-authorization-browser-first-party', () => {
569
603
 
570
604
  webex.authorization._startQRCodePolling(options);
571
605
  clock.tick(2000);
572
- await clock.runAllAsync()
606
+ await clock.runAllAsync();
573
607
 
574
608
  assert.calledOnce(webex.request);
575
609
 
@@ -577,7 +611,10 @@ describe('plugin-authorization-browser-first-party', () => {
577
611
 
578
612
  assert.equal(request.args[0].form.client_id, testClientId);
579
613
  assert.equal(request.args[0].form.device_code, testDeviceCode);
580
- assert.equal(request.args[0].form.grant_type, 'urn:ietf:params:oauth:grant-type:device_code');
614
+ assert.equal(
615
+ request.args[0].form.grant_type,
616
+ 'urn:ietf:params:oauth:grant-type:device_code'
617
+ );
581
618
 
582
619
  assert.calledOnce(webex.authorization.cancelQRCodePolling);
583
620
  assert.calledTwice(emitSpy);
@@ -593,23 +630,26 @@ describe('plugin-authorization-browser-first-party', () => {
593
630
  const options = {
594
631
  device_code: 'test-device-code',
595
632
  interval: 2,
596
- expires_in: 300
633
+ expires_in: 300,
597
634
  };
598
-
599
- webex.request.onFirstCall().rejects({statusCode: 428, body: {message: 'authorization_pending'}});
635
+
636
+ webex.request
637
+ .onFirstCall()
638
+ .rejects({statusCode: 428, body: {message: 'authorization_pending'}});
600
639
  webex.request.onSecondCall().resolves({statusCode: 200, body: {access_token: 'token'}});
601
640
  sinon.spy(webex.authorization, 'cancelQRCodePolling');
602
641
  const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
603
-
642
+
604
643
  webex.authorization._startQRCodePolling(options);
605
- clock.tick(4000);
606
- await clock.runAllAsync()
607
-
644
+ await clock.tickAsync(4000);
645
+ //await clock.runAllAsync()
646
+
608
647
  assert.calledTwice(webex.request);
609
648
  assert.calledOnce(webex.authorization.cancelQRCodePolling);
610
- assert.calledTwice(emitSpy);
611
- assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationSuccess');
612
- assert.equal(emitSpy.getCall(1).args[1].eventType, 'pollingCanceled');
649
+ assert.calledThrice(emitSpy);
650
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationPending');
651
+ assert.equal(emitSpy.getCall(1).args[1].eventType, 'authorizationSuccess');
652
+ assert.equal(emitSpy.getCall(2).args[1].eventType, 'pollingCanceled');
613
653
  clock.restore();
614
654
  });
615
655
 
@@ -619,23 +659,21 @@ describe('plugin-authorization-browser-first-party', () => {
619
659
  const options = {
620
660
  device_code: 'test-device-code',
621
661
  interval: 5,
622
- expires_in: 10
662
+ expires_in: 9,
623
663
  };
624
-
664
+
625
665
  webex.request.rejects({statusCode: 428, body: {message: 'authorizationPending'}});
626
666
  sinon.spy(webex.authorization, 'cancelQRCodePolling');
627
667
  const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
628
668
 
629
669
  webex.authorization._startQRCodePolling(options);
630
- clock.tick(10000);
631
- await clock.runAllAsync()
632
-
633
- assert.calledTwice(webex.request);
670
+ await clock.tickAsync(10_000);
671
+
672
+ assert.calledOnce(webex.request);
634
673
  assert.calledOnce(webex.authorization.cancelQRCodePolling);
635
- assert.calledThrice(emitSpy);
636
- assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationPending');
674
+ assert.calledTwice(emitSpy);
675
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationPending');
637
676
  assert.equal(emitSpy.getCall(1).args[1].eventType, 'authorizationFailure');
638
- assert.equal(emitSpy.getCall(2).args[1].eventType, 'pollingCanceled');
639
677
  clock.restore();
640
678
  });
641
679
 
@@ -644,54 +682,129 @@ describe('plugin-authorization-browser-first-party', () => {
644
682
  const options = {
645
683
  device_code: 'test-device-code',
646
684
  interval: 2,
647
- expires_in: 300
685
+ expires_in: 300,
648
686
  };
649
-
650
- webex.authorization.pollingRequest = 1;
687
+
688
+ webex.authorization.pollingTimer = 1;
651
689
 
652
690
  const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
653
691
  webex.authorization._startQRCodePolling(options);
654
692
 
655
693
  assert.calledOnce(emitSpy);
656
- assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationFailure');
657
- webex.authorization.pollingRequest = null;
694
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationFailure');
695
+ webex.authorization.pollingTimer = null;
696
+ });
697
+
698
+ it('should skip a interval when server ask for slow_down', async () => {
699
+ const clock = sinon.useFakeTimers();
700
+ const webex = makeWebex('http://example.com');
701
+ const options = {
702
+ device_code: 'test-device-code',
703
+ interval: 2,
704
+ expires_in: 300,
705
+ };
706
+
707
+ webex.request.onFirstCall().rejects({statusCode: 400, body: {message: 'slow_down'}});
708
+ webex.request.onSecondCall().resolves({statusCode: 200, body: {access_token: 'token'}});
709
+ sinon.spy(webex.authorization, 'cancelQRCodePolling');
710
+ const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
711
+
712
+ webex.authorization._startQRCodePolling(options);
713
+ await clock.tickAsync(4000);
714
+
715
+ // Request only once because of slow_down
716
+ assert.calledOnce(webex.request);
717
+
718
+ // Wait for next interval
719
+ await clock.tickAsync(2000);
720
+
721
+ assert.calledTwice(webex.request);
722
+ assert.calledOnce(webex.authorization.cancelQRCodePolling);
723
+ assert.calledTwice(emitSpy);
724
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'authorizationSuccess');
725
+ assert.equal(emitSpy.getCall(1).args[1].eventType, 'pollingCanceled');
726
+ clock.restore();
727
+ });
728
+
729
+ it('should ignore the response from the previous polling', async () => {
730
+ const clock = sinon.useFakeTimers();
731
+ const webex = makeWebex('http://example.com');
732
+ const options = {
733
+ device_code: 'test-device-code',
734
+ interval: 2,
735
+ expires_in: 300,
736
+ };
737
+
738
+ webex.request.onFirstCall().callsFake(() => {
739
+ return new Promise((resolve) => {
740
+ setTimeout(() => {
741
+ resolve({statusCode: 200, body: {access_token: 'token'}});
742
+ }, 1000);
743
+ });
744
+ });
745
+
746
+ webex.request
747
+ .onSecondCall()
748
+ .rejects({statusCode: 428, body: {message: 'authorizationPending'}});
749
+ sinon.spy(webex.authorization, 'cancelQRCodePolling');
750
+ const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
751
+
752
+ webex.authorization._startQRCodePolling(options);
753
+ await clock.tickAsync(2500);
754
+
755
+ webex.authorization.cancelQRCodePolling();
756
+
757
+ // Start new polling
758
+
759
+ webex.authorization._startQRCodePolling(options);
760
+
761
+ // Wait for next interval
762
+ await clock.tickAsync(3000);
763
+
764
+ assert.calledTwice(webex.request);
765
+ assert.calledOnce(webex.authorization.cancelQRCodePolling);
766
+ assert.calledTwice(emitSpy);
767
+ // authorizationSuccess event should not be emitted
768
+ assert.equal(emitSpy.getCall(0).args[1].eventType, 'pollingCanceled');
769
+ assert.equal(emitSpy.getCall(1).args[1].eventType, 'authorizationPending');
770
+ clock.restore();
658
771
  });
659
772
  });
660
773
 
661
774
  describe('#cancelQRCodePolling()', () => {
662
775
  it('should stop polling after cancellation', async () => {
663
- const clock = sinon.useFakeTimers();
664
- const webex = makeWebex('http://example.com');
665
- const options = {
666
- device_code: 'test-device-code',
667
- interval: 2,
668
- expires_in: 300
669
- };
670
-
671
- webex.request.rejects({statusCode: 428, body: {message: 'authorizationPending'}});
672
- const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
673
-
674
- webex.authorization._startQRCodePolling(options);
675
- // First poll
676
- clock.tick(2000);
677
- assert.calledOnce(webex.request);
678
-
679
- webex.authorization.cancelQRCodePolling();
680
- // Wait for next interval
681
- clock.tick(2000);
682
-
683
- const eventArgs = emitSpy.getCall(0).args;
684
-
685
- // Verify no additional requests were made
686
- assert.calledOnce(webex.request);
687
- assert.calledOnce(emitSpy);
688
- assert.equal(eventArgs[1].eventType, 'pollingCanceled');
689
- clock.restore();
690
- });
776
+ const clock = sinon.useFakeTimers();
777
+ const webex = makeWebex('http://example.com');
778
+ const options = {
779
+ device_code: 'test-device-code',
780
+ interval: 2,
781
+ expires_in: 300,
782
+ };
783
+
784
+ webex.request.rejects({statusCode: 428, body: {message: 'authorizationPending'}});
785
+ const emitSpy = sinon.spy(webex.authorization.eventEmitter, 'emit');
786
+
787
+ webex.authorization._startQRCodePolling(options);
788
+ // First poll
789
+ clock.tick(2000);
790
+ assert.calledOnce(webex.request);
791
+
792
+ webex.authorization.cancelQRCodePolling();
793
+ // Wait for next interval
794
+ clock.tick(2000);
795
+
796
+ const eventArgs = emitSpy.getCall(0).args;
797
+
798
+ // Verify no additional requests were made
799
+ assert.calledOnce(webex.request);
800
+ assert.calledOnce(emitSpy);
801
+ assert.equal(eventArgs[1].eventType, 'pollingCanceled');
802
+ clock.restore();
803
+ });
691
804
  it('should clear interval and reset polling request', () => {
692
805
  const clock = sinon.useFakeTimers();
693
806
  const webex = makeWebex('http://example.com');
694
-
807
+
695
808
  const options = {
696
809
  device_code: 'test_device_code',
697
810
  interval: 2,
@@ -699,22 +812,21 @@ describe('plugin-authorization-browser-first-party', () => {
699
812
  };
700
813
 
701
814
  webex.authorization._startQRCodePolling(options);
702
- assert.isDefined(webex.authorization.pollingRequest);
703
-
815
+ assert.isDefined(webex.authorization.pollingTimer);
816
+
704
817
  webex.authorization.cancelQRCodePolling();
705
- assert.isNull(webex.authorization.pollingRequest);
706
-
818
+ assert.isNull(webex.authorization.pollingTimer);
819
+
707
820
  clock.restore();
708
821
  });
709
-
822
+
710
823
  it('should handle cancellation when no polling is in progress', () => {
711
824
  const webex = makeWebex('http://example.com');
712
- assert.isNull(webex.authorization.pollingRequest);
713
-
825
+ assert.isNull(webex.authorization.pollingTimer);
826
+
714
827
  webex.authorization.cancelQRCodePolling();
715
- assert.isNull(webex.authorization.pollingRequest);
828
+ assert.isNull(webex.authorization.pollingTimer);
716
829
  });
717
-
718
830
  });
719
831
 
720
832
  describe('#_generateCodeChallenge', () => {
@@ -836,7 +948,7 @@ describe('plugin-authorization-browser-first-party', () => {
836
948
  const orgId = webex.authorization._extractOrgIdFromCode(code);
837
949
 
838
950
  assert.isUndefined(orgId);
839
- })
951
+ });
840
952
  });
841
953
  });
842
954
  });