@@ -16,6 +16,13 @@ import { fetching } from './node/fetch';
16
16
17
17
const CLIENT_ID = '01ab8ac9400c4e429b23' ;
18
18
const GITHUB_TOKEN_URL = 'https://vscode.dev/codeExchangeProxyEndpoints/github/login/oauth/access_token' ;
19
+
20
+ // This is the error message that we throw if the login was cancelled for any reason. Extensions
21
+ // calling `getSession` can handle this error to know that the user cancelled the login.
22
+ const CANCELLATION_ERROR = 'Cancelled' ;
23
+ // These error messages are internal and should not be shown to the user in any way.
24
+ const TIMED_OUT_ERROR = 'Timed out' ;
25
+ const USER_CANCELLATION_ERROR = 'User Cancelled' ;
19
26
const NETWORK_ERROR = 'network error' ;
20
27
21
28
const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect' ;
@@ -132,7 +139,7 @@ export class GitHubServer implements IGitHubServer {
132
139
: vscode . l10n . t ( 'You have not yet finished authorizing this extension to use GitHub. Would you like to keep trying?' ) ;
133
140
const result = await vscode . window . showWarningMessage ( message , yes , no ) ;
134
141
if ( result !== yes ) {
135
- throw new Error ( 'Cancelled' ) ;
142
+ throw new Error ( CANCELLATION_ERROR ) ;
136
143
}
137
144
} ;
138
145
@@ -146,7 +153,7 @@ export class GitHubServer implements IGitHubServer {
146
153
return await this . doLoginWithoutLocalServer ( scopes , nonce , callbackUri ) ;
147
154
} catch ( e ) {
148
155
this . _logger . error ( e ) ;
149
- userCancelled = e . message ?? e === 'User Cancelled' ;
156
+ userCancelled = e . message ?? e === USER_CANCELLATION_ERROR ;
150
157
}
151
158
}
152
159
@@ -163,8 +170,7 @@ export class GitHubServer implements IGitHubServer {
163
170
await promptToContinue ( ) ;
164
171
return await this . doLoginWithLocalServer ( scopes ) ;
165
172
} catch ( e ) {
166
- this . _logger . error ( e ) ;
167
- userCancelled = e . message ?? e === 'User Cancelled' ;
173
+ userCancelled = this . processLoginError ( e ) ;
168
174
}
169
175
}
170
176
@@ -174,8 +180,7 @@ export class GitHubServer implements IGitHubServer {
174
180
await promptToContinue ( ) ;
175
181
return await this . doLoginDeviceCodeFlow ( scopes ) ;
176
182
} catch ( e ) {
177
- this . _logger . error ( e ) ;
178
- userCancelled = e . message ?? e === 'User Cancelled' ;
183
+ userCancelled = this . processLoginError ( e ) ;
179
184
}
180
185
}
181
186
@@ -186,12 +191,11 @@ export class GitHubServer implements IGitHubServer {
186
191
await promptToContinue ( ) ;
187
192
return await this . doLoginWithPat ( scopes ) ;
188
193
} catch ( e ) {
189
- this . _logger . error ( e ) ;
190
- userCancelled = e . message ?? e === 'User Cancelled' ;
194
+ userCancelled = this . processLoginError ( e ) ;
191
195
}
192
196
}
193
197
194
- throw new Error ( userCancelled ? 'Cancelled' : 'No auth flow succeeded.' ) ;
198
+ throw new Error ( userCancelled ? CANCELLATION_ERROR : 'No auth flow succeeded.' ) ;
195
199
}
196
200
197
201
private async doLoginWithoutLocalServer ( scopes : string , nonce : string , callbackUri : vscode . Uri ) : Promise < string > {
@@ -232,8 +236,8 @@ export class GitHubServer implements IGitHubServer {
232
236
try {
233
237
return await Promise . race ( [
234
238
codeExchangePromise . promise ,
235
- new Promise < string > ( ( _ , reject ) => setTimeout ( ( ) => reject ( 'Timed out' ) , 300_000 ) ) , // 5min timeout
236
- promiseFromEvent < any , any > ( token . onCancellationRequested , ( _ , __ , reject ) => { reject ( 'User Cancelled' ) ; } ) . promise
239
+ new Promise < string > ( ( _ , reject ) => setTimeout ( ( ) => reject ( TIMED_OUT_ERROR ) , 300_000 ) ) , // 5min timeout
240
+ promiseFromEvent < any , any > ( token . onCancellationRequested , ( _ , __ , reject ) => { reject ( USER_CANCELLATION_ERROR ) ; } ) . promise
237
241
] ) ;
238
242
} finally {
239
243
this . _pendingNonces . delete ( scopes ) ;
@@ -273,8 +277,8 @@ export class GitHubServer implements IGitHubServer {
273
277
vscode . env . openExternal ( vscode . Uri . parse ( `http://127.0.0.1:${ port } /signin?nonce=${ encodeURIComponent ( server . nonce ) } ` ) ) ;
274
278
const { code } = await Promise . race ( [
275
279
server . waitForOAuthResponse ( ) ,
276
- new Promise < any > ( ( _ , reject ) => setTimeout ( ( ) => reject ( 'Timed out' ) , 300_000 ) ) , // 5min timeout
277
- promiseFromEvent < any , any > ( token . onCancellationRequested , ( _ , __ , reject ) => { reject ( 'User Cancelled' ) ; } ) . promise
280
+ new Promise < any > ( ( _ , reject ) => setTimeout ( ( ) => reject ( TIMED_OUT_ERROR ) , 300_000 ) ) , // 5min timeout
281
+ promiseFromEvent < any , any > ( token . onCancellationRequested , ( _ , __ , reject ) => { reject ( USER_CANCELLATION_ERROR ) ; } ) . promise
278
282
] ) ;
279
283
codeToExchange = code ;
280
284
} finally {
@@ -317,7 +321,7 @@ export class GitHubServer implements IGitHubServer {
317
321
} , button ) ;
318
322
319
323
if ( modalResult !== button ) {
320
- throw new Error ( 'User Cancelled' ) ;
324
+ throw new Error ( USER_CANCELLATION_ERROR ) ;
321
325
}
322
326
323
327
await vscode . env . clipboard . writeText ( json . user_code ) ;
@@ -340,14 +344,14 @@ export class GitHubServer implements IGitHubServer {
340
344
} , button ) ;
341
345
342
346
if ( modalResult !== button ) {
343
- throw new Error ( 'User Cancelled' ) ;
347
+ throw new Error ( USER_CANCELLATION_ERROR ) ;
344
348
}
345
349
346
350
const description = `${ vscode . env . appName } (${ scopes } )` ;
347
351
const uriToOpen = await vscode . env . asExternalUri ( this . baseUri . with ( { path : '/settings/tokens/new' , query : `description=${ description } &scopes=${ scopes . split ( ' ' ) . join ( ',' ) } ` } ) ) ;
348
352
await vscode . env . openExternal ( uriToOpen ) ;
349
353
const token = await vscode . window . showInputBox ( { placeHolder : `ghp_1a2b3c4...` , prompt : `GitHub Personal Access Token - ${ scopes } ` , ignoreFocusOut : true } ) ;
350
- if ( ! token ) { throw new Error ( 'User Cancelled' ) ; }
354
+ if ( ! token ) { throw new Error ( USER_CANCELLATION_ERROR ) ; }
351
355
352
356
const tokenScopes = await getScopes ( token , this . getServerUri ( '/' ) , this . _logger ) ; // Example: ['repo', 'user']
353
357
const scopesList = scopes . split ( ' ' ) ; // Example: 'read:user repo user:email'
@@ -392,7 +396,7 @@ export class GitHubServer implements IGitHubServer {
392
396
for ( let i = 0 ; i < attempts ; i ++ ) {
393
397
await new Promise ( resolve => setTimeout ( resolve , json . interval * 1000 ) ) ;
394
398
if ( token . isCancellationRequested ) {
395
- throw new Error ( 'User Cancelled' ) ;
399
+ throw new Error ( USER_CANCELLATION_ERROR ) ;
396
400
}
397
401
let accessTokenResult ;
398
402
try {
@@ -423,7 +427,7 @@ export class GitHubServer implements IGitHubServer {
423
427
return accessTokenJson . access_token ;
424
428
}
425
429
426
- throw new Error ( 'Cancelled' ) ;
430
+ throw new Error ( TIMED_OUT_ERROR ) ;
427
431
} ) ;
428
432
}
429
433
@@ -641,4 +645,12 @@ export class GitHubServer implements IGitHubServer {
641
645
// No-op
642
646
}
643
647
}
648
+
649
+ private processLoginError ( error : Error ) : boolean {
650
+ if ( error . message === CANCELLATION_ERROR ) {
651
+ throw error ;
652
+ }
653
+ this . _logger . error ( error . message ?? error ) ;
654
+ return error . message === USER_CANCELLATION_ERROR ;
655
+ }
644
656
}
0 commit comments