@@ -61,7 +61,7 @@ const DEFAULT_OAUTH_SCOPES = [
6161 */
6262export class OAuthSessionManager implements vscode . Disposable {
6363 private storedTokens : StoredOAuthTokens | undefined ;
64- private refreshInProgress = false ;
64+ private refreshPromise : Promise < TokenResponse > | null = null ;
6565 private lastRefreshAttempt = 0 ;
6666
6767 private pendingAuthReject : ( ( reason : Error ) => void ) | undefined ;
@@ -134,7 +134,7 @@ export class OAuthSessionManager implements vscode.Disposable {
134134 */
135135 private async clearTokenState ( ) : Promise < void > {
136136 this . storedTokens = undefined ;
137- this . refreshInProgress = false ;
137+ this . refreshPromise = null ;
138138 this . lastRefreshAttempt = 0 ;
139139 await this . secretsManager . setOAuthTokens ( undefined ) ;
140140 await this . secretsManager . setOAuthClientRegistration ( undefined ) ;
@@ -489,56 +489,64 @@ export class OAuthSessionManager implements vscode.Disposable {
489489
490490 /**
491491 * Refresh the access token using the stored refresh token.
492- * Uses a mutex to prevent concurrent refresh attempts.
492+ * Uses a shared promise to handle concurrent refresh attempts.
493493 */
494494 async refreshToken ( ) : Promise < TokenResponse > {
495- if ( this . refreshInProgress ) {
496- throw new Error ( "Token refresh already in progress" ) ;
495+ // If a refresh is already in progress, return the existing promise
496+ if ( this . refreshPromise ) {
497+ this . logger . debug (
498+ "Token refresh already in progress, waiting for result" ,
499+ ) ;
500+ return this . refreshPromise ;
497501 }
498502
499503 if ( ! this . storedTokens ?. refresh_token ) {
500504 throw new Error ( "No refresh token available" ) ;
501505 }
502506
503- this . refreshInProgress = true ;
507+ const refreshToken = this . storedTokens . refresh_token ;
508+ const accessToken = this . storedTokens . access_token ;
509+
504510 this . lastRefreshAttempt = Date . now ( ) ;
505511
506- try {
507- const { axiosInstance, metadata, registration } =
508- await this . prepareOAuthOperation (
509- this . deploymentUrl ,
510- this . storedTokens . access_token ,
511- ) ;
512+ // Create and store the refresh promise
513+ this . refreshPromise = ( async ( ) => {
514+ try {
515+ const { axiosInstance, metadata, registration } =
516+ await this . prepareOAuthOperation ( this . deploymentUrl , accessToken ) ;
512517
513- this . logger . debug ( "Refreshing access token" ) ;
518+ this . logger . debug ( "Refreshing access token" ) ;
514519
515- const params : RefreshTokenRequestParams = {
516- grant_type : REFRESH_GRANT_TYPE ,
517- refresh_token : this . storedTokens . refresh_token ,
518- client_id : registration . client_id ,
519- client_secret : registration . client_secret ,
520- } ;
520+ const params : RefreshTokenRequestParams = {
521+ grant_type : REFRESH_GRANT_TYPE ,
522+ refresh_token : refreshToken ,
523+ client_id : registration . client_id ,
524+ client_secret : registration . client_secret ,
525+ } ;
521526
522- const tokenRequest = toUrlSearchParams ( params ) ;
527+ const tokenRequest = toUrlSearchParams ( params ) ;
523528
524- const response = await axiosInstance . post < TokenResponse > (
525- metadata . token_endpoint ,
526- tokenRequest ,
527- {
528- headers : {
529- "Content-Type" : "application/x-www-form-urlencoded" ,
529+ const response = await axiosInstance . post < TokenResponse > (
530+ metadata . token_endpoint ,
531+ tokenRequest ,
532+ {
533+ headers : {
534+ "Content-Type" : "application/x-www-form-urlencoded" ,
535+ } ,
530536 } ,
531- } ,
532- ) ;
537+ ) ;
533538
534- this . logger . debug ( "Token refresh successful" ) ;
539+ this . logger . debug ( "Token refresh successful" ) ;
535540
536- await this . saveTokens ( response . data ) ;
541+ await this . saveTokens ( response . data ) ;
537542
538- return response . data ;
539- } finally {
540- this . refreshInProgress = false ;
541- }
543+ return response . data ;
544+ } finally {
545+ this . refreshPromise = null ;
546+ }
547+ } ) ( ) ;
548+
549+ return this . refreshPromise ;
542550 }
543551
544552 /**
@@ -579,7 +587,7 @@ export class OAuthSessionManager implements vscode.Disposable {
579587 if (
580588 ! this . isLoggedInWithOAuth ( ) ||
581589 ! this . storedTokens ?. refresh_token ||
582- this . refreshInProgress
590+ this . refreshPromise !== null
583591 ) {
584592 return false ;
585593 }
@@ -695,7 +703,7 @@ export class OAuthSessionManager implements vscode.Disposable {
695703 }
696704 this . pendingAuthReject = undefined ;
697705 this . storedTokens = undefined ;
698- this . refreshInProgress = false ;
706+ this . refreshPromise = null ;
699707 this . lastRefreshAttempt = 0 ;
700708
701709 this . logger . debug ( "OAuth session manager disposed" ) ;
0 commit comments