4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
6
import * as vscode from 'vscode' ;
7
- import * as querystring from 'querystring' ;
8
7
import * as path from 'path' ;
9
8
import { isSupportedEnvironment } from './utils' ;
10
9
import { generateCodeChallenge , generateCodeVerifier , randomUUID } from './cryptoUtils' ;
@@ -14,9 +13,10 @@ import { base64Decode } from './node/buffer';
14
13
import { fetching } from './node/fetch' ;
15
14
import { UriEventHandler } from './UriEventHandler' ;
16
15
import TelemetryReporter from '@vscode/extension-telemetry' ;
16
+ import { Environment } from '@azure/ms-rest-azure-env' ;
17
17
18
18
const redirectUrl = 'https://vscode.dev/redirect' ;
19
- const defaultLoginEndpointUrl = 'https://login.microsoftonline.com/' ;
19
+ const defaultActiveDirectoryEndpointUrl = Environment . AzureCloud . activeDirectoryEndpointUrl ;
20
20
const DEFAULT_CLIENT_ID = 'aebc6443-996d-45c2-90f0-388ff96faa56' ;
21
21
const DEFAULT_TENANT = 'organizations' ;
22
22
const MSA_TID = '9188040d-6c67-4c5b-b112-36a304b66dad' ;
@@ -102,7 +102,7 @@ export class AzureActiveDirectoryService {
102
102
private readonly _uriHandler : UriEventHandler ,
103
103
private readonly _tokenStorage : BetterTokenStorage < IStoredSession > ,
104
104
private readonly _telemetryReporter : TelemetryReporter ,
105
- private readonly _loginEndpointUrl : string = defaultLoginEndpointUrl
105
+ private readonly _env : Environment
106
106
) {
107
107
_context . subscriptions . push ( this . _tokenStorage . onDidChangeInOtherWindow ( ( e ) => this . checkForUpdates ( e ) ) ) ;
108
108
}
@@ -301,7 +301,7 @@ export class AzureActiveDirectoryService {
301
301
const runsRemote = vscode . env . remoteName !== undefined ;
302
302
const runsServerless = vscode . env . remoteName === undefined && vscode . env . uiKind === vscode . UIKind . Web ;
303
303
304
- if ( runsServerless && this . _loginEndpointUrl !== defaultLoginEndpointUrl ) {
304
+ if ( runsServerless && this . _env . activeDirectoryEndpointUrl !== defaultActiveDirectoryEndpointUrl ) {
305
305
throw new Error ( 'Sign in to non-public clouds is not supported on the web.' ) ;
306
306
}
307
307
@@ -338,7 +338,7 @@ export class AzureActiveDirectoryService {
338
338
code_challenge_method : 'S256' ,
339
339
code_challenge : codeChallenge ,
340
340
} ) . toString ( ) ;
341
- const loginUrl = `${ this . _loginEndpointUrl } ${ scopeData . tenant } /oauth2/v2.0/authorize?${ qs } ` ;
341
+ const loginUrl = new URL ( `${ scopeData . tenant } /oauth2/v2.0/authorize?${ qs } ` , this . _env . activeDirectoryEndpointUrl ) . toString ( ) ;
342
342
const server = new LoopbackAuthServer ( path . join ( __dirname , '../media' ) , loginUrl ) ;
343
343
await server . start ( ) ;
344
344
@@ -368,8 +368,8 @@ export class AzureActiveDirectoryService {
368
368
const state = encodeURIComponent ( callbackUri . toString ( true ) ) ;
369
369
const codeVerifier = generateCodeVerifier ( ) ;
370
370
const codeChallenge = await generateCodeChallenge ( codeVerifier ) ;
371
- const signInUrl = `${ this . _loginEndpointUrl } ${ scopeData . tenant } /oauth2/v2.0/authorize` ;
372
- const oauthStartQuery = new URLSearchParams ( {
371
+ const signInUrl = new URL ( `${ scopeData . tenant } /oauth2/v2.0/authorize` , this . _env . activeDirectoryEndpointUrl ) ;
372
+ signInUrl . search = new URLSearchParams ( {
373
373
response_type : 'code' ,
374
374
client_id : encodeURIComponent ( scopeData . clientId ) ,
375
375
response_mode : 'query' ,
@@ -379,8 +379,8 @@ export class AzureActiveDirectoryService {
379
379
prompt : 'select_account' ,
380
380
code_challenge_method : 'S256' ,
381
381
code_challenge : codeChallenge ,
382
- } ) ;
383
- const uri = vscode . Uri . parse ( ` ${ signInUrl } ? ${ oauthStartQuery . toString ( ) } ` ) ;
382
+ } ) . toString ( ) ;
383
+ const uri = vscode . Uri . parse ( signInUrl . toString ( ) ) ;
384
384
vscode . env . openExternal ( uri ) ;
385
385
386
386
let inputBox : vscode . InputBox | undefined ;
@@ -601,19 +601,15 @@ export class AzureActiveDirectoryService {
601
601
602
602
private async doRefreshToken ( refreshToken : string , scopeData : IScopeData , sessionId ?: string ) : Promise < IToken > {
603
603
this . _logger . info ( `Refreshing token for scopes: ${ scopeData . scopeStr } ` ) ;
604
- const postData = querystring . stringify ( {
604
+ const postData = new URLSearchParams ( {
605
605
refresh_token : refreshToken ,
606
606
client_id : scopeData . clientId ,
607
607
grant_type : 'refresh_token' ,
608
608
scope : scopeData . scopesToSend
609
- } ) ;
610
-
611
- const proxyEndpoints : { [ providerId : string ] : string } | undefined = await vscode . commands . executeCommand ( 'workbench.getCodeExchangeProxyEndpoints' ) ;
612
- const endpointUrl = proxyEndpoints ?. microsoft || this . _loginEndpointUrl ;
613
- const endpoint = `${ endpointUrl } ${ scopeData . tenant } /oauth2/v2.0/token` ;
609
+ } ) . toString ( ) ;
614
610
615
611
try {
616
- const json = await this . fetchTokenResponse ( endpoint , postData , scopeData ) ;
612
+ const json = await this . fetchTokenResponse ( postData , scopeData ) ;
617
613
const token = this . convertToTokenSync ( json , scopeData , sessionId ) ;
618
614
if ( token . expiresIn ) {
619
615
this . setSessionTimeout ( token . sessionId , token . refreshToken , scopeData , token . expiresIn * AzureActiveDirectoryService . REFRESH_TIMEOUT_MODIFIER ) ;
@@ -666,8 +662,9 @@ export class AzureActiveDirectoryService {
666
662
return new Promise ( ( resolve : ( value : vscode . AuthenticationSession ) => void , reject ) => {
667
663
uriEventListener = this . _uriHandler . event ( async ( uri : vscode . Uri ) => {
668
664
try {
669
- const query = querystring . parse ( uri . query ) ;
670
- let { code, nonce } = query ;
665
+ const query = new URLSearchParams ( uri . query ) ;
666
+ let code = query . get ( 'code' ) ;
667
+ let nonce = query . get ( 'nonce' ) ;
671
668
if ( Array . isArray ( code ) ) {
672
669
code = code [ 0 ] ;
673
670
}
@@ -735,28 +732,16 @@ export class AzureActiveDirectoryService {
735
732
this . _logger . info ( `Exchanging login code for token for scopes: ${ scopeData . scopeStr } ` ) ;
736
733
let token : IToken | undefined ;
737
734
try {
738
- const postData = querystring . stringify ( {
735
+ const postData = new URLSearchParams ( {
739
736
grant_type : 'authorization_code' ,
740
737
code : code ,
741
738
client_id : scopeData . clientId ,
742
739
scope : scopeData . scopesToSend ,
743
740
code_verifier : codeVerifier ,
744
741
redirect_uri : redirectUrl
745
- } ) ;
746
-
747
- let endpointUrl : string ;
742
+ } ) . toString ( ) ;
748
743
749
- if ( this . _loginEndpointUrl !== defaultLoginEndpointUrl ) {
750
- // If this is for sovereign clouds, don't try using the proxy endpoint, which supports only public cloud
751
- endpointUrl = this . _loginEndpointUrl ;
752
- } else {
753
- const proxyEndpoints : { [ providerId : string ] : string } | undefined = await vscode . commands . executeCommand ( 'workbench.getCodeExchangeProxyEndpoints' ) ;
754
- endpointUrl = proxyEndpoints ?. microsoft || this . _loginEndpointUrl ;
755
- }
756
-
757
- const endpoint = `${ endpointUrl } ${ scopeData . tenant } /oauth2/v2.0/token` ;
758
-
759
- const json = await this . fetchTokenResponse ( endpoint , postData , scopeData ) ;
744
+ const json = await this . fetchTokenResponse ( postData , scopeData ) ;
760
745
this . _logger . info ( `Exchanging login code for token (for scopes: ${ scopeData . scopeStr } ) succeeded!` ) ;
761
746
token = this . convertToTokenSync ( json , scopeData ) ;
762
747
} catch ( e ) {
@@ -772,7 +757,17 @@ export class AzureActiveDirectoryService {
772
757
return await this . convertToSession ( token , scopeData ) ;
773
758
}
774
759
775
- private async fetchTokenResponse ( endpoint : string , postData : string , scopeData : IScopeData ) : Promise < ITokenResponse > {
760
+ private async fetchTokenResponse ( postData : string , scopeData : IScopeData ) : Promise < ITokenResponse > {
761
+ let endpointUrl : string ;
762
+ if ( this . _env . activeDirectoryEndpointUrl !== defaultActiveDirectoryEndpointUrl ) {
763
+ // If this is for sovereign clouds, don't try using the proxy endpoint, which supports only public cloud
764
+ endpointUrl = this . _env . activeDirectoryEndpointUrl ;
765
+ } else {
766
+ const proxyEndpoints : { [ providerId : string ] : string } | undefined = await vscode . commands . executeCommand ( 'workbench.getCodeExchangeProxyEndpoints' ) ;
767
+ endpointUrl = proxyEndpoints ?. microsoft || this . _env . activeDirectoryEndpointUrl ;
768
+ }
769
+ const endpoint = new URL ( `${ scopeData . tenant } /oauth2/v2.0/token` , endpointUrl ) ;
770
+
776
771
let attempts = 0 ;
777
772
while ( attempts <= 3 ) {
778
773
attempts ++ ;
@@ -869,7 +864,7 @@ export class AzureActiveDirectoryService {
869
864
refreshToken : token . refreshToken ,
870
865
scope : token . scope ,
871
866
account : token . account ,
872
- endpoint : this . _loginEndpointUrl ,
867
+ endpoint : this . _env . activeDirectoryEndpointUrl ,
873
868
} ) ;
874
869
this . _logger . info ( `Stored token for scopes: ${ scopeData . scopeStr } ` ) ;
875
870
}
@@ -933,9 +928,9 @@ export class AzureActiveDirectoryService {
933
928
934
929
private sessionMatchesEndpoint ( session : IStoredSession ) : boolean {
935
930
// For older sessions with no endpoint set, it can be assumed to be the default endpoint
936
- session . endpoint ||= defaultLoginEndpointUrl ;
931
+ session . endpoint ||= defaultActiveDirectoryEndpointUrl ;
937
932
938
- return session . endpoint === this . _loginEndpointUrl ;
933
+ return session . endpoint === this . _env . activeDirectoryEndpointUrl ;
939
934
}
940
935
941
936
//#endregion
0 commit comments