@@ -13,7 +13,7 @@ import { INTERNAL_AUTH_PROVIDER_PREFIX } from '../../services/authentication/com
13
13
import { createDecorator } from '../../../platform/instantiation/common/instantiation.js' ;
14
14
import { IExtHostRpcService } from './extHostRpcService.js' ;
15
15
import { URI , UriComponents } from '../../../base/common/uri.js' ;
16
- import { fetchDynamicRegistration , getClaimsFromJWT , IAuthorizationJWTClaims , IAuthorizationProtectedResourceMetadata , IAuthorizationServerMetadata , IAuthorizationTokenResponse , isAuthorizationTokenResponse } from '../../../base/common/oauth.js' ;
16
+ import { AuthorizationErrorType , fetchDynamicRegistration , getClaimsFromJWT , IAuthorizationJWTClaims , IAuthorizationProtectedResourceMetadata , IAuthorizationServerMetadata , IAuthorizationTokenResponse , isAuthorizationErrorResponse , isAuthorizationTokenResponse } from '../../../base/common/oauth.js' ;
17
17
import { IExtHostWindow } from './extHostWindow.js' ;
18
18
import { IExtHostInitDataService } from './extHostInitDataService.js' ;
19
19
import { ILogger , ILoggerService , ILogService } from '../../../platform/log/common/log.js' ;
@@ -213,13 +213,19 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
213
213
214
214
// Use the sequencer to ensure dynamic provider registration is serialized
215
215
await this . _providerOperations . queue ( provider . id , async ( ) => {
216
- const disposable = provider . onDidChangeSessions ( e => this . _proxy . $sendDidChangeSessions ( provider . id , e ) ) ;
217
216
this . _authenticationProviders . set (
218
217
provider . id ,
219
218
{
220
219
label : provider . label ,
221
220
provider,
222
- disposable : Disposable . from ( provider , disposable ) ,
221
+ disposable : Disposable . from (
222
+ provider ,
223
+ provider . onDidChangeSessions ( e => this . _proxy . $sendDidChangeSessions ( provider . id , e ) ) ,
224
+ provider . onDidChangeClientId ( ( ) => this . _proxy . $sendDidChangeDynamicProviderInfo ( {
225
+ providerId : provider . id ,
226
+ clientId : provider . clientId
227
+ } ) )
228
+ ) ,
223
229
options : { supportsMultipleAccounts : false }
224
230
}
225
231
) ;
@@ -256,6 +262,9 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
256
262
private _onDidChangeSessions = new Emitter < vscode . AuthenticationProviderAuthenticationSessionsChangeEvent > ( ) ;
257
263
readonly onDidChangeSessions = this . _onDidChangeSessions . event ;
258
264
265
+ private readonly _onDidChangeClientId = new Emitter < void > ( ) ;
266
+ readonly onDidChangeClientId = this . _onDidChangeClientId . event ;
267
+
259
268
private readonly _tokenStore : TokenStore ;
260
269
261
270
protected readonly _createFlows : Array < {
@@ -276,7 +285,7 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
276
285
readonly authorizationServer : URI ,
277
286
protected readonly _serverMetadata : IAuthorizationServerMetadata ,
278
287
protected readonly _resourceMetadata : IAuthorizationProtectedResourceMetadata | undefined ,
279
- readonly clientId : string ,
288
+ protected _clientId : string ,
280
289
onDidDynamicAuthProviderTokensChange : Emitter < { authProviderId : string ; clientId : string ; tokens : IAuthorizationToken [ ] } > ,
281
290
initialTokens : IAuthorizationToken [ ] ,
282
291
) {
@@ -292,7 +301,7 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
292
301
this . _disposable = new DisposableStore ( ) ;
293
302
this . _disposable . add ( this . _onDidChangeSessions ) ;
294
303
const scopedEvent = Event . chain ( onDidDynamicAuthProviderTokensChange . event , $ => $
295
- . filter ( e => e . authProviderId === this . id && e . clientId === clientId )
304
+ . filter ( e => e . authProviderId === this . id && e . clientId === _clientId )
296
305
. map ( e => e . tokens )
297
306
) ;
298
307
this . _tokenStore = this . _disposable . add ( new TokenStore (
@@ -311,6 +320,10 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
311
320
} ] ;
312
321
}
313
322
323
+ get clientId ( ) : string {
324
+ return this . _clientId ;
325
+ }
326
+
314
327
async getSessions ( scopes : readonly string [ ] | undefined , _options : vscode . AuthenticationProviderSessionOptions ) : Promise < vscode . AuthenticationSession [ ] > {
315
328
this . _logger . info ( `Getting sessions for scopes: ${ scopes ?. join ( ' ' ) ?? 'all' } ` ) ;
316
329
if ( ! scopes ) {
@@ -455,7 +468,7 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
455
468
456
469
// Prepare the authorization request URL
457
470
const authorizationUrl = new URL ( this . _serverMetadata . authorization_endpoint ! ) ;
458
- authorizationUrl . searchParams . append ( 'client_id' , this . clientId ) ;
471
+ authorizationUrl . searchParams . append ( 'client_id' , this . _clientId ) ;
459
472
authorizationUrl . searchParams . append ( 'response_type' , 'code' ) ;
460
473
authorizationUrl . searchParams . append ( 'state' , state . toString ( ) ) ;
461
474
authorizationUrl . searchParams . append ( 'code_challenge' , codeChallenge ) ;
@@ -546,7 +559,7 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
546
559
}
547
560
548
561
const tokenRequest = new URLSearchParams ( ) ;
549
- tokenRequest . append ( 'client_id' , this . clientId ) ;
562
+ tokenRequest . append ( 'client_id' , this . _clientId ) ;
550
563
tokenRequest . append ( 'grant_type' , 'authorization_code' ) ;
551
564
tokenRequest . append ( 'code' , code ) ;
552
565
tokenRequest . append ( 'redirect_uri' , redirectUri ) ;
@@ -569,6 +582,10 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
569
582
const result = await response . json ( ) ;
570
583
if ( isAuthorizationTokenResponse ( result ) ) {
571
584
return result ;
585
+ } else if ( isAuthorizationErrorResponse ( result ) && result . error === AuthorizationErrorType . InvalidClient ) {
586
+ this . _logger . warn ( `Client ID (${ this . _clientId } ) was invalid, generated a new one.` ) ;
587
+ await this . _generateNewClientId ( ) ;
588
+ throw new Error ( `Client ID was invalid, generated a new one. Please try again.` ) ;
572
589
}
573
590
throw new Error ( `Invalid authorization token response: ${ JSON . stringify ( result ) } ` ) ;
574
591
}
@@ -579,7 +596,7 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
579
596
}
580
597
581
598
const tokenRequest = new URLSearchParams ( ) ;
582
- tokenRequest . append ( 'client_id' , this . clientId ) ;
599
+ tokenRequest . append ( 'client_id' , this . _clientId ) ;
583
600
tokenRequest . append ( 'grant_type' , 'refresh_token' ) ;
584
601
tokenRequest . append ( 'refresh_token' , refreshToken ) ;
585
602
@@ -592,20 +609,30 @@ export class DynamicAuthProvider implements vscode.AuthenticationProvider {
592
609
body : tokenRequest . toString ( )
593
610
} ) ;
594
611
595
- if ( ! response . ok ) {
596
- const text = await response . text ( ) ;
597
- throw new Error ( `Token exchange failed: ${ response . status } ${ response . statusText } - ${ text } ` ) ;
598
- }
599
-
600
612
const result = await response . json ( ) ;
601
613
if ( isAuthorizationTokenResponse ( result ) ) {
602
614
return {
603
615
...result ,
604
616
created_at : Date . now ( ) ,
605
617
} ;
618
+ } else if ( isAuthorizationErrorResponse ( result ) && result . error === AuthorizationErrorType . InvalidClient ) {
619
+ this . _logger . warn ( `Client ID (${ this . _clientId } ) was invalid, generated a new one.` ) ;
620
+ await this . _generateNewClientId ( ) ;
621
+ throw new Error ( `Client ID was invalid, generated a new one. Please try again.` ) ;
606
622
}
607
623
throw new Error ( `Invalid authorization token response: ${ JSON . stringify ( result ) } ` ) ;
608
624
}
625
+
626
+ protected async _generateNewClientId ( ) : Promise < void > {
627
+ try {
628
+ const registration = await fetchDynamicRegistration ( this . _serverMetadata , this . _initData . environment . appName , this . _resourceMetadata ?. scopes_supported ) ;
629
+ this . _clientId = registration . client_id ;
630
+ this . _onDidChangeClientId . fire ( ) ;
631
+ } catch ( err ) {
632
+ this . _logger . error ( `Failed to fetch new client ID: ${ err } ` ) ;
633
+ throw new Error ( `Failed to fetch new client ID: ${ err } ` ) ;
634
+ }
635
+ }
609
636
}
610
637
611
638
type IAuthorizationToken = IAuthorizationTokenResponse & {
0 commit comments