@@ -21,6 +21,8 @@ import type {
21
21
OAuthResponse ,
22
22
} from './types' ;
23
23
24
+ import Deferred from './utils/deferred' ;
25
+
24
26
import {
25
27
decodeToken ,
26
28
getRealmUrl ,
@@ -100,7 +102,7 @@ export class KeycloakClient implements KeycloakInstance {
100
102
101
103
private logWarn = this . createLogger ( console . warn ) ;
102
104
103
- private refreshTokenPromise ?: Promise < boolean > ;
105
+ private refreshQueue : Array < Deferred < boolean > > = [ ] ;
104
106
105
107
private useNonce ?: boolean ;
106
108
@@ -420,6 +422,85 @@ export class KeycloakClient implements KeycloakInstance {
420
422
return expiresIn < 0 ;
421
423
}
422
424
425
+ private async runUpdateToken (
426
+ minValidity : number ,
427
+ deffered : Deferred < boolean >
428
+ ) {
429
+ let shouldRefreshToken : boolean = false ;
430
+
431
+ if ( minValidity === - 1 ) {
432
+ shouldRefreshToken = true ;
433
+ this . logInfo ( '[KEYCLOAK] Refreshing token: forced refresh' ) ;
434
+ } else if ( ! this . tokenParsed || this . isTokenExpired ( minValidity ) ) {
435
+ shouldRefreshToken = true ;
436
+ this . logInfo ( '[KEYCLOAK] Refreshing token: token expired' ) ;
437
+ }
438
+
439
+ if ( ! shouldRefreshToken ) {
440
+ deffered . resolve ( false ) ;
441
+ } else {
442
+ const tokenUrl = this . endpoints ! . token ( ) ;
443
+
444
+ const params = new Map < string , string > ( ) ;
445
+ params . set ( 'client_id' , this . clientId ! ) ;
446
+ params . set ( 'grant_type' , 'refresh_token' ) ;
447
+ params . set ( 'refresh_token' , this . refreshToken ! ) ;
448
+
449
+ this . refreshQueue . push ( deffered ) ;
450
+
451
+ if ( this . refreshQueue . length === 1 ) {
452
+ let timeLocal = new Date ( ) . getTime ( ) ;
453
+
454
+ try {
455
+ const tokenResponse = await this . adapter ! . refreshTokens (
456
+ tokenUrl ,
457
+ formatQuerystringParameters ( params )
458
+ ) ;
459
+
460
+ if ( tokenResponse . error ) {
461
+ this . clearToken ( ) ;
462
+ throw new Error ( tokenResponse . error ) ;
463
+ } else {
464
+ this . logInfo ( '[KEYCLOAK] Token refreshed' ) ;
465
+
466
+ timeLocal = ( timeLocal + new Date ( ) . getTime ( ) ) / 2 ;
467
+
468
+ this . setToken (
469
+ tokenResponse . access_token ,
470
+ tokenResponse . refresh_token ,
471
+ tokenResponse . id_token ,
472
+ timeLocal
473
+ ) ;
474
+
475
+ // Notify onAuthRefreshSuccess event handler if set
476
+ this . onAuthRefreshSuccess && this . onAuthRefreshSuccess ( ) ;
477
+
478
+ for (
479
+ var p = this . refreshQueue . pop ( ) ;
480
+ p != null ;
481
+ p = this . refreshQueue . pop ( )
482
+ ) {
483
+ p . resolve ( true ) ;
484
+ }
485
+ }
486
+ } catch ( err ) {
487
+ this . logWarn ( '[KEYCLOAK] Failed to refresh token' ) ;
488
+
489
+ // Notify onAuthRefreshError event handler if set
490
+ this . onAuthRefreshError && this . onAuthRefreshError ( ) ;
491
+
492
+ for (
493
+ var p = this . refreshQueue . pop ( ) ;
494
+ p != null ;
495
+ p = this . refreshQueue . pop ( )
496
+ ) {
497
+ p . reject ( true ) ;
498
+ }
499
+ }
500
+ }
501
+ }
502
+ }
503
+
423
504
/**
424
505
* If the token expires within `minValidity` seconds, the token is refreshed.
425
506
* If the session status iframe is enabled, the session status is also
@@ -439,79 +520,16 @@ export class KeycloakClient implements KeycloakInstance {
439
520
* });
440
521
*/
441
522
public async updateToken ( minValidity : number = 5 ) : Promise < boolean > {
442
- if ( ! this . refreshToken ) {
443
- throw new Error ( 'missing refreshToken' ) ;
444
- }
523
+ const deffered = new Deferred < boolean > ( ) ;
445
524
446
- if ( this . refreshTokenPromise ) {
447
- return this . refreshTokenPromise ;
525
+ if ( ! this . refreshToken ) {
526
+ deffered . reject ( 'missing refreshToken' ) ;
527
+ return deffered . getPromise ( ) ;
448
528
}
449
529
450
- this . refreshTokenPromise = new Promise < boolean > ( async ( resolve , reject ) => {
451
- let shouldRefreshToken : boolean = false ;
452
-
453
- if ( minValidity === - 1 ) {
454
- shouldRefreshToken = true ;
455
- this . logInfo ( '[KEYCLOAK] Refreshing token: forced refresh' ) ;
456
- } else if ( ! this . tokenParsed || this . isTokenExpired ( minValidity ) ) {
457
- shouldRefreshToken = true ;
458
- this . logInfo ( '[KEYCLOAK] Refreshing token: token expired' ) ;
459
- }
460
-
461
- if ( ! shouldRefreshToken ) {
462
- resolve ( false ) ;
463
-
464
- this . refreshTokenPromise = undefined ;
465
-
466
- return ;
467
- }
468
-
469
- const tokenUrl = this . endpoints ! . token ( ) ;
470
-
471
- const params = new Map < string , string > ( ) ;
472
- params . set ( 'client_id' , this . clientId ! ) ;
473
- params . set ( 'grant_type' , 'refresh_token' ) ;
474
- params . set ( 'refresh_token' , this . refreshToken ! ) ;
475
-
476
- let timeLocal = new Date ( ) . getTime ( ) ;
477
-
478
- try {
479
- const tokenResponse = await this . adapter ! . refreshTokens (
480
- tokenUrl ,
481
- formatQuerystringParameters ( params )
482
- ) ;
483
-
484
- this . logInfo ( '[KEYCLOAK] Token refreshed' ) ;
485
-
486
- timeLocal = ( timeLocal + new Date ( ) . getTime ( ) ) / 2 ;
487
-
488
- this . setToken (
489
- tokenResponse . access_token ,
490
- tokenResponse . refresh_token ,
491
- tokenResponse . id_token ,
492
- timeLocal
493
- ) ;
494
-
495
- // Notify onAuthRefreshSuccess event handler if set
496
- this . onAuthRefreshSuccess && this . onAuthRefreshSuccess ( ) ;
497
-
498
- resolve ( true ) ;
499
- } catch ( err ) {
500
- this . logWarn ( '[KEYCLOAK] Failed to refresh token' ) ;
501
-
502
- // Clear tokens
503
- this . clearToken ( ) ;
504
-
505
- // Notify onAuthRefreshError event handler if set
506
- this . onAuthRefreshError && this . onAuthRefreshError ( ) ;
507
-
508
- reject ( ) ;
509
- }
510
-
511
- this . refreshTokenPromise = undefined ;
512
- } ) ;
530
+ this . runUpdateToken ( minValidity , deffered ) ;
513
531
514
- return this . refreshTokenPromise ;
532
+ return deffered . getPromise ( ) ;
515
533
}
516
534
517
535
/**
0 commit comments