@@ -204,13 +204,9 @@ export class AzureActiveDirectoryService {
204204 } , 1000 * 30 ) ;
205205 }
206206
207- private convertToSession ( token : IToken ) : vscode . AuthenticationSession {
208- return {
209- id : token . sessionId ,
210- getAccessToken : ( ) => this . resolveAccessToken ( token ) ,
211- account : token . account ,
212- scopes : token . scope . split ( ' ' )
213- } ;
207+ private async convertToSession ( token : IToken ) : Promise < vscode . AuthenticationSession2 > {
208+ const resolvedToken = await this . resolveAccessToken ( token ) ;
209+ return new vscode . AuthenticationSession2 ( token . sessionId , resolvedToken , token . account , token . scope . split ( ' ' ) ) ;
214210 }
215211
216212 private async resolveAccessToken ( token : IToken ) : Promise < string > {
@@ -243,77 +239,81 @@ export class AzureActiveDirectoryService {
243239 }
244240 }
245241
246- get sessions ( ) : vscode . AuthenticationSession [ ] {
247- return this . _tokens . map ( token => this . convertToSession ( token ) ) ;
242+ get sessions ( ) : Promise < vscode . AuthenticationSession2 [ ] > {
243+ return Promise . all ( this . _tokens . map ( token => this . convertToSession ( token ) ) ) ;
248244 }
249245
250- public async login ( scope : string ) : Promise < void > {
246+ public async login ( scope : string ) : Promise < vscode . AuthenticationSession2 > {
251247 Logger . info ( 'Logging in...' ) ;
248+ return new Promise ( async ( resolve , reject ) => {
249+ if ( vscode . env . uiKind === vscode . UIKind . Web ) {
250+ resolve ( this . loginWithoutLocalServer ( scope ) ) ;
251+ return ;
252+ }
252253
253- if ( vscode . env . uiKind === vscode . UIKind . Web ) {
254- await this . loginWithoutLocalServer ( scope ) ;
255- return ;
256- }
254+ const nonce = crypto . randomBytes ( 16 ) . toString ( 'base64' ) ;
255+ const { server, redirectPromise, codePromise } = createServer ( nonce ) ;
257256
258- const nonce = crypto . randomBytes ( 16 ) . toString ( 'base64' ) ;
259- const { server, redirectPromise, codePromise } = createServer ( nonce ) ;
257+ let token : IToken | undefined ;
258+ try {
259+ const port = await startServer ( server ) ;
260+ vscode . env . openExternal ( vscode . Uri . parse ( `http://localhost:${ port } /signin?nonce=${ encodeURIComponent ( nonce ) } ` ) ) ;
261+
262+ const redirectReq = await redirectPromise ;
263+ if ( 'err' in redirectReq ) {
264+ const { err, res } = redirectReq ;
265+ res . writeHead ( 302 , { Location : `/?error=${ encodeURIComponent ( err && err . message || 'Unknown error' ) } ` } ) ;
266+ res . end ( ) ;
267+ throw err ;
268+ }
260269
261- let token : IToken | undefined ;
262- try {
263- const port = await startServer ( server ) ;
264- vscode . env . openExternal ( vscode . Uri . parse ( `http://localhost:${ port } /signin?nonce=${ encodeURIComponent ( nonce ) } ` ) ) ;
265-
266- const redirectReq = await redirectPromise ;
267- if ( 'err' in redirectReq ) {
268- const { err, res } = redirectReq ;
269- res . writeHead ( 302 , { Location : `/?error=${ encodeURIComponent ( err && err . message || 'Unknown error' ) } ` } ) ;
270- res . end ( ) ;
271- throw err ;
272- }
270+ const host = redirectReq . req . headers . host || '' ;
271+ const updatedPortStr = ( / ^ [ ^ : ] + : ( \d + ) $ / . exec ( Array . isArray ( host ) ? host [ 0 ] : host ) || [ ] ) [ 1 ] ;
272+ const updatedPort = updatedPortStr ? parseInt ( updatedPortStr , 10 ) : port ;
273273
274- const host = redirectReq . req . headers . host || '' ;
275- const updatedPortStr = ( / ^ [ ^ : ] + : ( \d + ) $ / . exec ( Array . isArray ( host ) ? host [ 0 ] : host ) || [ ] ) [ 1 ] ;
276- const updatedPort = updatedPortStr ? parseInt ( updatedPortStr , 10 ) : port ;
274+ const state = `${ updatedPort } ,${ encodeURIComponent ( nonce ) } ` ;
277275
278- const state = `${ updatedPort } ,${ encodeURIComponent ( nonce ) } ` ;
276+ const codeVerifier = toBase64UrlEncoding ( crypto . randomBytes ( 32 ) . toString ( 'base64' ) ) ;
277+ const codeChallenge = toBase64UrlEncoding ( crypto . createHash ( 'sha256' ) . update ( codeVerifier ) . digest ( 'base64' ) ) ;
278+ const loginUrl = `${ loginEndpointUrl } ${ tenant } /oauth2/v2.0/authorize?response_type=code&response_mode=query&client_id=${ encodeURIComponent ( clientId ) } &redirect_uri=${ encodeURIComponent ( redirectUrl ) } &state=${ state } &scope=${ encodeURIComponent ( scope ) } &prompt=select_account&code_challenge_method=S256&code_challenge=${ codeChallenge } ` ;
279279
280- const codeVerifier = toBase64UrlEncoding ( crypto . randomBytes ( 32 ) . toString ( 'base64' ) ) ;
281- const codeChallenge = toBase64UrlEncoding ( crypto . createHash ( 'sha256' ) . update ( codeVerifier ) . digest ( 'base64' ) ) ;
282- const loginUrl = `${ loginEndpointUrl } ${ tenant } /oauth2/v2.0/authorize?response_type=code&response_mode=query&client_id=${ encodeURIComponent ( clientId ) } &redirect_uri=${ encodeURIComponent ( redirectUrl ) } &state=${ state } &scope=${ encodeURIComponent ( scope ) } &prompt=select_account&code_challenge_method=S256&code_challenge=${ codeChallenge } ` ;
280+ await redirectReq . res . writeHead ( 302 , { Location : loginUrl } ) ;
281+ redirectReq . res . end ( ) ;
283282
284- await redirectReq . res . writeHead ( 302 , { Location : loginUrl } ) ;
285- redirectReq . res . end ( ) ;
283+ const codeRes = await codePromise ;
284+ const res = codeRes . res ;
286285
287- const codeRes = await codePromise ;
288- const res = codeRes . res ;
286+ try {
287+ if ( 'err' in codeRes ) {
288+ throw codeRes . err ;
289+ }
290+ token = await this . exchangeCodeForToken ( codeRes . code , codeVerifier , scope ) ;
291+ this . setToken ( token , scope ) ;
292+ Logger . info ( 'Login successful' ) ;
293+ res . writeHead ( 302 , { Location : '/' } ) ;
294+ const session = await this . convertToSession ( token ) ;
295+ resolve ( session ) ;
296+ res . end ( ) ;
297+ } catch ( err ) {
298+ res . writeHead ( 302 , { Location : `/?error=${ encodeURIComponent ( err && err . message || 'Unknown error' ) } ` } ) ;
299+ res . end ( ) ;
300+ reject ( err . message ) ;
301+ }
302+ } catch ( e ) {
303+ Logger . error ( e . message ) ;
289304
290- try {
291- if ( 'err' in codeRes ) {
292- throw codeRes . err ;
305+ // If the error was about starting the server, try directly hitting the login endpoint instead
306+ if ( e . message === 'Error listening to server' || e . message === 'Closed' || e . message === 'Timeout waiting for port' ) {
307+ await this . loginWithoutLocalServer ( scope ) ;
293308 }
294- token = await this . exchangeCodeForToken ( codeRes . code , codeVerifier , scope ) ;
295- this . setToken ( token , scope ) ;
296- Logger . info ( 'Login successful' ) ;
297- res . writeHead ( 302 , { Location : '/' } ) ;
298- res . end ( ) ;
299- } catch ( err ) {
300- res . writeHead ( 302 , { Location : `/?error=${ encodeURIComponent ( err && err . message || 'Unknown error' ) } ` } ) ;
301- res . end ( ) ;
302- throw new Error ( err . message ) ;
303- }
304- } catch ( e ) {
305- Logger . error ( e . message ) ;
306309
307- // If the error was about starting the server, try directly hitting the login endpoint instead
308- if ( e . message === 'Error listening to server' || e . message === 'Closed' || e . message === 'Timeout waiting for port' ) {
309- await this . loginWithoutLocalServer ( scope ) ;
310+ reject ( e . message ) ;
311+ } finally {
312+ setTimeout ( ( ) => {
313+ server . close ( ) ;
314+ } , 5000 ) ;
310315 }
311- throw new Error ( e . message ) ;
312- } finally {
313- setTimeout ( ( ) => {
314- server . close ( ) ;
315- } , 5000 ) ;
316- }
316+ } ) ;
317317 }
318318
319319 private getCallbackEnvironment ( callbackUri : vscode . Uri ) : string {
@@ -333,7 +333,7 @@ export class AzureActiveDirectoryService {
333333 }
334334 }
335335
336- private async loginWithoutLocalServer ( scope : string ) : Promise < IToken > {
336+ private async loginWithoutLocalServer ( scope : string ) : Promise < vscode . AuthenticationSession2 > {
337337 const callbackUri = await vscode . env . asExternalUri ( vscode . Uri . parse ( `${ vscode . env . uriScheme } ://vscode.microsoft-authentication` ) ) ;
338338 const nonce = crypto . randomBytes ( 16 ) . toString ( 'base64' ) ;
339339 const port = ( callbackUri . authority . match ( / : ( [ 0 - 9 ] * ) $ / ) || [ ] ) [ 1 ] || ( callbackUri . scheme === 'https' ? 443 : 80 ) ;
@@ -348,7 +348,7 @@ export class AzureActiveDirectoryService {
348348 } ) ;
349349 vscode . env . openExternal ( uri ) ;
350350
351- const timeoutPromise = new Promise ( ( _ : ( value : IToken ) => void , reject ) => {
351+ const timeoutPromise = new Promise ( ( _ : ( value : vscode . AuthenticationSession2 ) => void , reject ) => {
352352 const wait = setTimeout ( ( ) => {
353353 clearTimeout ( wait ) ;
354354 reject ( 'Login timed out.' ) ;
@@ -358,9 +358,9 @@ export class AzureActiveDirectoryService {
358358 return Promise . race ( [ this . handleCodeResponse ( state , codeVerifier , scope ) , timeoutPromise ] ) ;
359359 }
360360
361- private async handleCodeResponse ( state : string , codeVerifier : string , scope : string ) {
361+ private async handleCodeResponse ( state : string , codeVerifier : string , scope : string ) : Promise < vscode . AuthenticationSession2 > {
362362 let uriEventListener : vscode . Disposable ;
363- return new Promise ( ( resolve : ( value : IToken ) => void , reject ) => {
363+ return new Promise ( ( resolve : ( value : vscode . AuthenticationSession2 ) => void , reject ) => {
364364 uriEventListener = this . _uriHandler . event ( async ( uri : vscode . Uri ) => {
365365 try {
366366 const query = parseQuery ( uri ) ;
@@ -374,7 +374,8 @@ export class AzureActiveDirectoryService {
374374 const token = await this . exchangeCodeForToken ( code , codeVerifier , scope ) ;
375375 this . setToken ( token , scope ) ;
376376
377- resolve ( token ) ;
377+ const session = await this . convertToSession ( token ) ;
378+ resolve ( session ) ;
378379 } catch ( err ) {
379380 reject ( err ) ;
380381 }
0 commit comments