@@ -18,8 +18,9 @@ const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56';
18
18
const tenant = 'organizations' ;
19
19
20
20
interface IToken {
21
- expiresIn : string ; // How long access token is valid, in seconds
22
- accessToken : string ;
21
+ accessToken ?: string ; // When unable to refresh due to network problems, the access token becomes undefined
22
+
23
+ expiresIn ?: string ; // How long access token is valid, in seconds
23
24
refreshToken : string ;
24
25
25
26
accountName : string ;
@@ -41,6 +42,7 @@ interface IStoredSession {
41
42
id : string ;
42
43
refreshToken : string ;
43
44
scope : string ; // Scopes are alphabetized and joined with a space
45
+ accountName : string ;
44
46
}
45
47
46
48
function parseQuery ( uri : vscode . Uri ) {
@@ -93,7 +95,20 @@ export class AzureActiveDirectoryService {
93
95
try {
94
96
await this . refreshToken ( session . refreshToken , session . scope ) ;
95
97
} catch ( e ) {
96
- this . handleTokenRefreshFailure ( e , session . id , session . refreshToken , session . scope , false ) ;
98
+ if ( e . message === REFRESH_NETWORK_FAILURE ) {
99
+ const didSucceedOnRetry = await this . handleRefreshNetworkError ( session . id , session . refreshToken , session . scope ) ;
100
+ if ( ! didSucceedOnRetry ) {
101
+ this . _tokens . push ( {
102
+ accessToken : undefined ,
103
+ refreshToken : session . refreshToken ,
104
+ accountName : session . accountName ,
105
+ scope : session . scope ,
106
+ sessionId : session . id
107
+ } ) ;
108
+ }
109
+ } else {
110
+ await this . logout ( session . id ) ;
111
+ }
97
112
}
98
113
} ) ;
99
114
@@ -115,7 +130,8 @@ export class AzureActiveDirectoryService {
115
130
return {
116
131
id : token . sessionId ,
117
132
refreshToken : token . refreshToken ,
118
- scope : token . scope
133
+ scope : token . scope ,
134
+ accountName : token . accountName
119
135
} ;
120
136
} ) ;
121
137
@@ -136,7 +152,11 @@ export class AzureActiveDirectoryService {
136
152
await this . refreshToken ( session . refreshToken , session . scope ) ;
137
153
didChange = true ;
138
154
} catch ( e ) {
139
- this . handleTokenRefreshFailure ( e , session . id , session . refreshToken , session . scope , false ) ;
155
+ if ( e . message === REFRESH_NETWORK_FAILURE ) {
156
+ // Ignore, will automatically retry on next poll.
157
+ } else {
158
+ await this . logout ( session . id ) ;
159
+ }
140
160
}
141
161
}
142
162
} ) ;
@@ -175,7 +195,7 @@ export class AzureActiveDirectoryService {
175
195
private convertToSession ( token : IToken ) : vscode . AuthenticationSession {
176
196
return {
177
197
id : token . sessionId ,
178
- accessToken : ( ) => Promise . resolve ( token . accessToken ) ,
198
+ accessToken : ( ) => ! token . accessToken ? Promise . reject ( 'Unavailable due to network problems' ) : Promise . resolve ( token . accessToken ) ,
179
199
accountName : token . accountName ,
180
200
scopes : token . scope . split ( ' ' )
181
201
} ;
@@ -339,14 +359,21 @@ export class AzureActiveDirectoryService {
339
359
340
360
this . clearSessionTimeout ( token . sessionId ) ;
341
361
342
- this . _refreshTimeouts . set ( token . sessionId , setTimeout ( async ( ) => {
343
- try {
344
- await this . refreshToken ( token . refreshToken , scope ) ;
345
- onDidChangeSessions . fire ( ) ;
346
- } catch ( e ) {
347
- this . handleTokenRefreshFailure ( e , token . sessionId , token . refreshToken , scope , true ) ;
348
- }
349
- } , 1000 * ( parseInt ( token . expiresIn ) - 30 ) ) ) ;
362
+ if ( token . expiresIn ) {
363
+ this . _refreshTimeouts . set ( token . sessionId , setTimeout ( async ( ) => {
364
+ try {
365
+ await this . refreshToken ( token . refreshToken , scope ) ;
366
+ onDidChangeSessions . fire ( ) ;
367
+ } catch ( e ) {
368
+ if ( e . message === REFRESH_NETWORK_FAILURE ) {
369
+ await this . handleRefreshNetworkError ( token . sessionId , token . refreshToken , scope ) ;
370
+ } else {
371
+ await this . logout ( token . sessionId ) ;
372
+ onDidChangeSessions . fire ( ) ;
373
+ }
374
+ }
375
+ } , 1000 * ( parseInt ( token . expiresIn ) - 30 ) ) ) ;
376
+ }
350
377
351
378
this . storeTokenData ( ) ;
352
379
}
@@ -480,40 +507,35 @@ export class AzureActiveDirectoryService {
480
507
this . clearSessionTimeout ( sessionId ) ;
481
508
}
482
509
483
- private async handleTokenRefreshFailure ( e : Error , sessionId : string , refreshToken : string , scope : string , sendChangeEvent : boolean ) : Promise < void > {
484
- if ( e . message === REFRESH_NETWORK_FAILURE ) {
485
- this . handleRefreshNetworkError ( sessionId , refreshToken , scope ) ;
486
- } else {
487
- await this . logout ( sessionId ) ;
488
- if ( sendChangeEvent ) {
489
- onDidChangeSessions . fire ( ) ;
510
+ private handleRefreshNetworkError ( sessionId : string , refreshToken : string , scope : string , attempts : number = 1 ) : Promise < boolean > {
511
+ return new Promise ( ( resolve , _ ) => {
512
+ if ( attempts === 3 ) {
513
+ Logger . error ( 'Token refresh failed after 3 attempts' ) ;
514
+ return resolve ( false ) ;
490
515
}
491
- }
492
- }
493
516
494
- private handleRefreshNetworkError ( sessionId : string , refreshToken : string , scope : string , attempts : number = 1 ) {
495
- if ( attempts === 5 ) {
496
- Logger . error ( 'Token refresh failed after 5 attempts' ) ;
497
- return ;
498
- }
517
+ if ( attempts == = 1 ) {
518
+ const token = this . _tokens . find ( token => token . sessionId === sessionId ) ;
519
+ if ( token ) {
520
+ token . accessToken = undefined ;
521
+ }
499
522
500
- if ( attempts === 1 ) {
501
- this . removeInMemorySessionData ( sessionId ) ;
502
- onDidChangeSessions . fire ( ) ;
503
- }
523
+ onDidChangeSessions . fire ( ) ;
524
+ }
504
525
505
- const delayBeforeRetry = 5 * attempts * attempts ;
526
+ const delayBeforeRetry = 5 * attempts * attempts ;
506
527
507
- this . clearSessionTimeout ( sessionId ) ;
528
+ this . clearSessionTimeout ( sessionId ) ;
508
529
509
- this . _refreshTimeouts . set ( sessionId , setTimeout ( async ( ) => {
510
- try {
511
- await this . refreshToken ( refreshToken , scope ) ;
512
- onDidChangeSessions . fire ( ) ;
513
- } catch ( e ) {
514
- await this . handleRefreshNetworkError ( sessionId , refreshToken , scope , attempts + 1 ) ;
515
- }
516
- } , 1000 * delayBeforeRetry ) ) ;
530
+ this . _refreshTimeouts . set ( sessionId , setTimeout ( async ( ) => {
531
+ try {
532
+ await this . refreshToken ( refreshToken , scope ) ;
533
+ return resolve ( true ) ;
534
+ } catch ( e ) {
535
+ return resolve ( await this . handleRefreshNetworkError ( sessionId , refreshToken , scope , attempts + 1 ) ) ;
536
+ }
537
+ } , 1000 * delayBeforeRetry ) ) ;
538
+ } ) ;
517
539
}
518
540
519
541
public async logout ( sessionId : string ) {
0 commit comments