@@ -10,12 +10,13 @@ import * as vscode from 'vscode';
10
10
import * as nls from 'vscode-nls' ;
11
11
import { v4 as uuid } from 'uuid' ;
12
12
import fetch , { Response } from 'node-fetch' ;
13
- import { createServer , startServer } from './authServer' ;
14
13
import { Keychain } from './keychain' ;
15
14
import Logger from './logger' ;
16
15
import { toBase64UrlEncoding } from './utils' ;
17
16
import { sha256 } from './env/node/sha256' ;
18
17
import { BetterTokenStorage , IDidChangeInOtherWindowEvent } from './betterSecretStorage' ;
18
+ import { LoopbackAuthServer } from './authServer' ;
19
+ import path = require( 'path' ) ;
19
20
20
21
const localize = nls . loadMessageBundle ( ) ;
21
22
@@ -238,63 +239,42 @@ export class AzureActiveDirectoryService {
238
239
}
239
240
240
241
private async createSessionWithLocalServer ( scopeData : IScopeData ) {
241
- const nonce = randomBytes ( 16 ) . toString ( 'base64' ) ;
242
- const { server, redirectPromise, codePromise } = createServer ( nonce ) ;
242
+ const codeVerifier = toBase64UrlEncoding ( randomBytes ( 32 ) . toString ( 'base64' ) ) ;
243
+ const codeChallenge = toBase64UrlEncoding ( await sha256 ( codeVerifier ) ) ;
244
+ const qs = querystring . stringify ( {
245
+ response_type : 'code' ,
246
+ response_mode : 'query' ,
247
+ client_id : scopeData . clientId ,
248
+ redirect_uri : redirectUrl ,
249
+ scope : scopeData . scopesToSend ,
250
+ prompt : 'select_account' ,
251
+ code_challenge_method : 'S256' ,
252
+ code_challenge : codeChallenge ,
253
+ } ) ;
254
+ const loginUrl = `${ loginEndpointUrl } ${ scopeData . tenant } /oauth2/v2.0/authorize?${ qs } ` ;
255
+ const server = new LoopbackAuthServer ( path . join ( __dirname , '../media' ) , loginUrl ) ;
256
+ await server . start ( ) ;
257
+ server . state = `${ server . port } ,${ encodeURIComponent ( server . nonce ) } ` ;
243
258
244
- let token : IToken | undefined ;
259
+ let codeToExchange ;
245
260
try {
246
- const port = await startServer ( server ) ;
247
- vscode . env . openExternal ( vscode . Uri . parse ( `http://localhost:${ port } /signin?nonce=${ encodeURIComponent ( nonce ) } ` ) ) ;
248
-
249
- const redirectReq = await redirectPromise ;
250
- if ( 'err' in redirectReq ) {
251
- const { err, res } = redirectReq ;
252
- res . writeHead ( 302 , { Location : `/?error=${ encodeURIComponent ( err && err . message || 'Unknown error' ) } ` } ) ;
253
- res . end ( ) ;
254
- throw err ;
255
- }
256
-
257
- const host = redirectReq . req . headers . host || '' ;
258
- const updatedPortStr = ( / ^ [ ^ : ] + : ( \d + ) $ / . exec ( Array . isArray ( host ) ? host [ 0 ] : host ) || [ ] ) [ 1 ] ;
259
- const updatedPort = updatedPortStr ? parseInt ( updatedPortStr , 10 ) : port ;
260
-
261
- const state = `${ updatedPort } ,${ encodeURIComponent ( nonce ) } ` ;
262
-
263
- const codeVerifier = toBase64UrlEncoding ( randomBytes ( 32 ) . toString ( 'base64' ) ) ;
264
- const codeChallenge = toBase64UrlEncoding ( await sha256 ( codeVerifier ) ) ;
265
-
266
- const loginUrl = `${ loginEndpointUrl } ${ scopeData . tenant } /oauth2/v2.0/authorize?response_type=code&response_mode=query&client_id=${ encodeURIComponent ( scopeData . clientId ) } &redirect_uri=${ encodeURIComponent ( redirectUrl ) } &state=${ state } &scope=${ encodeURIComponent ( scopeData . scopesToSend ) } &prompt=select_account&code_challenge_method=S256&code_challenge=${ codeChallenge } ` ;
267
-
268
- redirectReq . res . writeHead ( 302 , { Location : loginUrl } ) ;
269
- redirectReq . res . end ( ) ;
270
-
271
- const codeRes = await codePromise ;
272
- const res = codeRes . res ;
273
-
274
- try {
275
- if ( 'err' in codeRes ) {
276
- throw codeRes . err ;
277
- }
278
- token = await this . exchangeCodeForToken ( codeRes . code , codeVerifier , scopeData ) ;
279
- if ( token . expiresIn ) {
280
- this . setSessionTimeout ( token . sessionId , token . refreshToken , scopeData , token . expiresIn * AzureActiveDirectoryService . REFRESH_TIMEOUT_MODIFIER ) ;
281
- }
282
- await this . setToken ( token , scopeData ) ;
283
- Logger . info ( `Login successful for scopes: ${ scopeData . scopeStr } ` ) ;
284
- res . writeHead ( 302 , { Location : '/' } ) ;
285
- const session = await this . convertToSession ( token ) ;
286
- return session ;
287
- } catch ( err ) {
288
- res . writeHead ( 302 , { Location : `/?error=${ encodeURIComponent ( err && err . message || 'Unknown error' ) } ` } ) ;
289
- throw err ;
290
- } finally {
291
- res . end ( ) ;
292
- }
261
+ vscode . env . openExternal ( vscode . Uri . parse ( `http://localhost:${ server . port } /signin?nonce=${ encodeURIComponent ( server . nonce ) } ` ) ) ;
262
+ const { code } = await server . waitForOAuthResponse ( ) ;
263
+ codeToExchange = code ;
293
264
} finally {
294
265
setTimeout ( ( ) => {
295
- server . close ( ) ;
266
+ void server . stop ( ) ;
296
267
} , 5000 ) ;
297
268
}
269
+
270
+ const token = await this . exchangeCodeForToken ( codeToExchange , codeVerifier , scopeData ) ;
271
+ if ( token . expiresIn ) {
272
+ this . setSessionTimeout ( token . sessionId , token . refreshToken , scopeData , token . expiresIn * AzureActiveDirectoryService . REFRESH_TIMEOUT_MODIFIER ) ;
273
+ }
274
+ await this . setToken ( token , scopeData ) ;
275
+ Logger . info ( `Login successful for scopes: ${ scopeData . scopeStr } ` ) ;
276
+ const session = await this . convertToSession ( token ) ;
277
+ return session ;
298
278
}
299
279
300
280
private async createSessionWithoutLocalServer ( scopeData : IScopeData ) : Promise < vscode . AuthenticationSession > {
0 commit comments