@@ -14,6 +14,7 @@ import {cloneDeep, isEmpty, omit} from 'lodash';
14
14
import uuid from 'uuid' ;
15
15
import base64url from 'crypto-js/enc-base64url' ;
16
16
import CryptoJS from 'crypto-js' ;
17
+ import { interfaceExtends } from '@babel/types' ;
17
18
18
19
// Necessary to require lodash this way in order to stub
19
20
// methods in the unit test
@@ -67,17 +68,49 @@ const Authorization = WebexPlugin.extend({
67
68
68
69
namespace : 'Credentials' ,
69
70
71
+ /**
72
+ * EventEmitter for authorization events
73
+ * @instance
74
+ * @memberof AuthorizationBrowserFirstParty
75
+ * @type {EventEmitter }
76
+ * @public
77
+ */
78
+ eventEmitter : new EventEmitter ( ) ,
70
79
71
80
/**
72
- * Stores the interval ID for QR code polling
81
+ * Stores the timer ID for QR code polling
73
82
* @instance
74
83
* @memberof AuthorizationBrowserFirstParty
75
84
* @type {?number }
76
85
* @private
77
86
*/
78
- pollingRequest : null ,
87
+ pollingTimer : null ,
88
+ /**
89
+ * Stores the expiration timer ID for QR code polling
90
+ * @instance
91
+ * @memberof AuthorizationBrowserFirstParty
92
+ * @type {?number }
93
+ * @private
94
+ */
95
+ pollingExpirationTimer : null ,
79
96
80
- eventEmitter : new EventEmitter ( ) ,
97
+ /**
98
+ * Monotonically increasing id to identify the current polling request
99
+ * @instance
100
+ * @memberof AuthorizationBrowserFirstParty
101
+ * @type {number }
102
+ * @private
103
+ */
104
+ pollingId : 0 ,
105
+
106
+ /**
107
+ * Identifier for the current polling request
108
+ * @instance
109
+ * @memberof AuthorizationBrowserFirstParty
110
+ * @type {?number }
111
+ * @private
112
+ */
113
+ currentPollingId : null ,
81
114
82
115
/**
83
116
* Initializer
@@ -260,7 +293,7 @@ const Authorization = WebexPlugin.extend({
260
293
* @emits #qRCodeLogin
261
294
*/
262
295
initQRCodeLogin ( ) {
263
- if ( this . pollingRequest ) {
296
+ if ( this . pollingTimer ) {
264
297
this . eventEmitter . emit ( 'qRCodeLogin' , {
265
298
eventType : 'getUserCodeFailure' ,
266
299
data : { message : 'There is already a polling request' } ,
@@ -291,7 +324,7 @@ const Authorization = WebexPlugin.extend({
291
324
userCode : user_code ,
292
325
verificationUri : verification_uri ,
293
326
verificationUriComplete : verification_uri_complete ,
294
- }
327
+ } ,
295
328
} ) ;
296
329
// if device authorization success, then start to poll server to check whether the user has completed authorization
297
330
this . _startQRCodePolling ( res . body ) ;
@@ -320,23 +353,29 @@ const Authorization = WebexPlugin.extend({
320
353
return ;
321
354
}
322
355
323
- if ( this . pollingRequest ) {
356
+ if ( this . pollingTimer ) {
324
357
this . eventEmitter . emit ( 'qRCodeLogin' , {
325
358
eventType : 'authorizationFailure' ,
326
359
data : { message : 'There is already a polling request' } ,
327
360
} ) ;
328
361
return ;
329
362
}
330
363
331
- const { device_code : deviceCode , interval = 2 , expires_in : expiresIn = 300 } = options ;
364
+ const { device_code : deviceCode , expires_in : expiresIn = 300 } = options ;
365
+ let interval = options . interval ?? 2 ;
332
366
333
- let attempts = 0 ;
334
- const maxAttempts = expiresIn / interval ;
367
+ this . pollingExpirationTimer = setTimeout ( ( ) => {
368
+ this . cancelQRCodePolling ( false ) ;
369
+ this . eventEmitter . emit ( 'qRCodeLogin' , {
370
+ eventType : 'authorizationFailure' ,
371
+ data : { message : 'Authorization timed out' } ,
372
+ } ) ;
373
+ } , expiresIn * 1000 ) ;
335
374
336
- this . pollingRequest = setInterval ( ( ) => {
337
- attempts += 1 ;
375
+ const polling = ( ) => {
376
+ this . pollingId += 1 ;
377
+ this . currentPollingId = this . pollingId ;
338
378
339
- const currentAttempts = attempts ;
340
379
this . webex
341
380
. request ( {
342
381
method : 'POST' ,
@@ -354,7 +393,8 @@ const Authorization = WebexPlugin.extend({
354
393
} ,
355
394
} )
356
395
. then ( ( res ) => {
357
- if ( this . pollingRequest === null ) return ;
396
+ // if the pollingId has changed, it means that the polling request has been canceled
397
+ if ( this . currentPollingId !== this . pollingId ) return ;
358
398
359
399
this . eventEmitter . emit ( 'qRCodeLogin' , {
360
400
eventType : 'authorizationSuccess' ,
@@ -363,34 +403,40 @@ const Authorization = WebexPlugin.extend({
363
403
this . cancelQRCodePolling ( ) ;
364
404
} )
365
405
. catch ( ( res ) => {
366
- if ( this . pollingRequest === null ) return ;
406
+ // if the pollingId has changed, it means that the polling request has been canceled
407
+ if ( this . currentPollingId !== this . pollingId ) return ;
367
408
368
- if ( currentAttempts >= maxAttempts ) {
369
- this . eventEmitter . emit ( 'qRCodeLogin' , {
370
- eventType : 'authorizationFailure' ,
371
- data : { message : 'Authorization timed out' }
372
- } ) ;
373
- this . cancelQRCodePolling ( ) ;
409
+ // When server sends 400 status code with message 'slow_down', it means that last request happened too soon.
410
+ // So, skip one interval and then poll again.
411
+ if ( res . statusCode === 400 && res . body . message === 'slow_down' ) {
412
+ schedulePolling ( interval * 2 ) ;
374
413
return ;
375
414
}
415
+
376
416
// if the statusCode is 428 which means that the authorization request is still pending
377
417
// as the end user hasn't yet completed the user-interaction steps. So keep polling.
378
418
if ( res . statusCode === 428 ) {
379
419
this . eventEmitter . emit ( 'qRCodeLogin' , {
380
420
eventType : 'authorizationPending' ,
381
- data : res . body
421
+ data : res . body ,
382
422
} ) ;
423
+ schedulePolling ( interval ) ;
383
424
return ;
384
425
}
385
426
386
427
this . cancelQRCodePolling ( ) ;
387
428
388
429
this . eventEmitter . emit ( 'qRCodeLogin' , {
389
430
eventType : 'authorizationFailure' ,
390
- data : res . body
431
+ data : res . body ,
391
432
} ) ;
392
433
} ) ;
393
- } , interval * 1000 ) ;
434
+ } ;
435
+
436
+ const schedulePolling = ( interval ) =>
437
+ ( this . pollingTimer = setTimeout ( polling , interval * 1000 ) ) ;
438
+
439
+ schedulePolling ( interval ) ;
394
440
} ,
395
441
396
442
/**
@@ -399,14 +445,19 @@ const Authorization = WebexPlugin.extend({
399
445
* @memberof AuthorizationBrowserFirstParty
400
446
* @returns {void }
401
447
*/
402
- cancelQRCodePolling ( ) {
403
- if ( this . pollingRequest ) {
404
- clearInterval ( this . pollingRequest ) ;
448
+ cancelQRCodePolling ( withCancelEvent = true ) {
449
+ if ( this . pollingTimer && withCancelEvent ) {
405
450
this . eventEmitter . emit ( 'qRCodeLogin' , {
406
451
eventType : 'pollingCanceled' ,
407
452
} ) ;
408
- this . pollingRequest = null ;
409
453
}
454
+
455
+ this . currentPollingId = null ;
456
+
457
+ clearTimeout ( this . pollingExpirationTimer ) ;
458
+ this . pollingExpirationTimer = null ;
459
+ clearTimeout ( this . pollingTimer ) ;
460
+ this . pollingTimer = null ;
410
461
} ,
411
462
412
463
/**
0 commit comments