@@ -16,7 +16,7 @@ import { codicon, getIcon } from '../shared/icons'
16
16
import { Commands } from '../shared/vscode/commands2'
17
17
import { createQuickPick , DataQuickPickItem , showQuickPick } from '../shared/ui/pickerPrompter'
18
18
import { isValidResponse } from '../shared/wizards/wizard'
19
- import { CancellationError } from '../shared/utilities/timeoutUtils'
19
+ import { CancellationError , Timeout } from '../shared/utilities/timeoutUtils'
20
20
import { errorCode , formatError , ToolkitError , UnknownError } from '../shared/errors'
21
21
import { getCache } from './sso/cache'
22
22
import { createFactoryFunction , Mutable } from '../shared/utilities/tsUtils'
@@ -306,6 +306,7 @@ export class Auth implements AuthService, ConnectionManager {
306
306
readonly #validationErrors = new Map < Connection [ 'id' ] , Error > ( )
307
307
readonly #onDidChangeActiveConnection = new vscode . EventEmitter < StatefulConnection | undefined > ( )
308
308
readonly #onDidChangeConnectionState = new vscode . EventEmitter < ConnectionStateChangeEvent > ( )
309
+ readonly #invalidCredentialsTimeouts = new Map < Connection [ 'id' ] , Timeout > ( )
309
310
public readonly onDidChangeActiveConnection = this . #onDidChangeActiveConnection. event
310
311
public readonly onDidChangeConnectionState = this . #onDidChangeConnectionState. event
311
312
@@ -495,6 +496,7 @@ export class Auth implements AuthService, ConnectionManager {
495
496
const profile = await this . store . updateProfile ( id , { connectionState } )
496
497
if ( connectionState !== 'invalid' ) {
497
498
this . #validationErrors. delete ( id )
499
+ this . #invalidCredentialsTimeouts. get ( id ) ?. dispose ( )
498
500
}
499
501
500
502
if ( this . #activeConnection?. id === id ) {
@@ -680,10 +682,16 @@ export class Auth implements AuthService, ConnectionManager {
680
682
cause : this . #validationErrors. get ( id ) ,
681
683
} )
682
684
}
683
- // TODO: cancellable notification?
684
685
if ( previousState === 'valid' ) {
686
+ const timeout = new Timeout ( 60000 )
687
+ this . #invalidCredentialsTimeouts. set ( id , timeout )
688
+
685
689
const message = localize ( 'aws.auth.invalidConnection' , 'Connection is invalid or expired, login again?' )
686
- const resp = await vscode . window . showInformationMessage ( message , localizedText . yes , localizedText . no )
690
+ const resp = await Promise . race ( [
691
+ vscode . window . showInformationMessage ( message , localizedText . yes , localizedText . no ) ,
692
+ timeout . promisify ( ) ,
693
+ ] )
694
+
687
695
if ( resp !== localizedText . yes ) {
688
696
throw new ToolkitError ( 'User cancelled login' , {
689
697
cancelled : true ,
@@ -701,6 +709,16 @@ export class Auth implements AuthService, ConnectionManager {
701
709
return
702
710
}
703
711
712
+ // Clear anything stuck in an 'authenticating...' state
713
+ // This can rarely happen when closing VS Code during authentication
714
+ await Promise . all (
715
+ this . store . listProfiles ( ) . map ( async ( [ id , profile ] ) => {
716
+ if ( profile . metadata . connectionState === 'authenticating' ) {
717
+ await this . store . updateProfile ( id , { connectionState : 'invalid' } )
718
+ }
719
+ } )
720
+ )
721
+
704
722
// Use the environment token if available
705
723
if ( getCodeCatalystDevEnvId ( ) !== undefined ) {
706
724
const profile = createBuilderIdProfile ( )
0 commit comments