@@ -11,6 +11,7 @@ import { ParsedIniData } from '@smithy/types'
1111import { chain } from '@aws-sdk/property-provider'
1212import { fromInstanceMetadata , fromContainerMetadata } from '@smithy/credential-provider-imds'
1313import { fromEnv } from '@aws-sdk/credential-provider-env'
14+ import * as localizedText from '../../shared/localizedText'
1415import { getLogger } from '../../shared/logger/logger'
1516import { getStringHash } from '../../shared/utilities/textUtilities'
1617import { getMfaTokenFromUser , resolveProviderWithCancel } from '../credentials/utils'
@@ -59,6 +60,96 @@ function isSsoProfile(profile: Profile): boolean {
5960 )
6061}
6162
63+ export async function handleInvalidConsoleCredentials (
64+ error : Error ,
65+ profileName : string ,
66+ region : string
67+ ) : Promise < never > {
68+ getLogger ( ) . error ( 'Console login authentication failed for profile %s in region %s: %O' , profileName , region , error )
69+
70+ // Indicates that a VS Code window reload is required to reinitialize credential providers
71+ // and avoid using stale console session credentials when login cache and in-memory state diverge.
72+ let requiresVscodeReloadForCredentials = false
73+ if (
74+ error . message . includes ( 'Your session has expired' ) ||
75+ error . message . includes ( 'Failed to load a token for session' ) ||
76+ error . message . includes ( 'Failed to load token from' )
77+ ) {
78+ requiresVscodeReloadForCredentials = true
79+ // Ask for user confirmation before refreshing
80+ const response = await vscode . window . showInformationMessage (
81+ `Unable to use your console credentials for profile "${ profileName } ". Would you like to retry?` ,
82+ localizedText . retry ,
83+ localizedText . cancel
84+ )
85+
86+ if ( response !== localizedText . retry ) {
87+ throw ToolkitError . chain ( error , 'User cancelled console credentials token refresh.' , {
88+ code : 'LoginSessionRefreshCancelled' ,
89+ cancelled : true ,
90+ } )
91+ }
92+
93+ getLogger ( ) . info ( 'Re-authenticating using console credentials for profile %s' , profileName )
94+ // Execute the console login command with the existing profile and region
95+ try {
96+ await vscode . commands . executeCommand ( 'aws.toolkit.auth.consoleLogin' , profileName , region )
97+ } catch ( _ ) {
98+ void vscode . window . showErrorMessage (
99+ `Unable to refresh your AWS credentials. Please run 'aws login --profile ${ profileName } ' in your terminal, then reload VS Code to continue.`
100+ )
101+ }
102+ }
103+
104+ if ( error . message . includes ( 'does not contain login_session' ) ) {
105+ // The credential provider was created before the CLI wrote the new login session to disk.
106+ // This happens when you run console login and immediately try to use the connection.
107+ // A window reload is needed to pick up the newly created session.
108+ requiresVscodeReloadForCredentials = true
109+ }
110+
111+ if ( requiresVscodeReloadForCredentials ) {
112+ getLogger ( ) . info (
113+ `Reloading window to sync with updated credentials cache using connection for profile: ${ profileName } `
114+ )
115+ const reloadResponse = await vscode . window . showInformationMessage (
116+ `Credentials for "${ profileName } " were updated. A window reload is required to apply them. Save your work before continuing. Reload now?` ,
117+ localizedText . yes ,
118+ localizedText . no
119+ )
120+ if ( reloadResponse === localizedText . yes ) {
121+ // At this point, the console credential cache on disk has been updated (via AWS CLI login),
122+ // but the in-memory credential providers used by the Toolkit / AWS SDK were already
123+ // constructed earlier and continue to reference stale credentials.
124+ //
125+ // Notes on behavior:
126+ // - Console credentials are read once when the provider is created and are not reloaded
127+ // dynamically at runtime.
128+ // - Removing or recreating connections/profiles does not rebuild the underlying provider.
129+ // - Filesystem watchers may detect cache changes, but provider instances still hold
130+ // the originally loaded credentials.
131+ // - Attempting to swap providers at runtime can introduce incompatibilities between
132+ // legacy credential shims and AWS SDK v3 providers.
133+ //
134+ // Authentication flow (simplified):
135+ // aws login (CLI) -> writes ~/.aws/login/cache
136+ // Toolkit -> constructs credential provider (snapshots credentials in memory)
137+ // SDK calls -> continue using in-memory credentials until provider is reinitialized
138+ //
139+ // A VS Code window reload is the only safe and deterministic way to fully reinitialize
140+ // credential providers and ensure the updated console session credentials are used.
141+ await vscode . commands . executeCommand ( 'workbench.action.reloadWindow' )
142+ }
143+ throw ToolkitError . chain ( error , 'Console credentials require window reload' , {
144+ code : 'FromLoginCredentialProviderError' ,
145+ } )
146+ }
147+
148+ throw ToolkitError . chain ( error , 'Console credentials error' , {
149+ code : 'FromLoginCredentialProviderError' ,
150+ } )
151+ }
152+
62153/**
63154 * Represents one profile from the AWS Shared Credentials files.
64155 */
@@ -400,67 +491,21 @@ export class SharedCredentialsProvider implements CredentialsProvider {
400491 const baseProvider = fromLoginCredentials ( {
401492 profile : this . profileName ,
402493 clientConfig : {
494+ // Console session profiles created by 'aws login' may not have a region property
495+ // The AWS CLI's philosophy is to treat global options like --region as per-invocation overrides
496+ // rather than persistent configuration, minimizing what gets permanently stored in profiles
497+ // and deferring configuration decisions until the actual command execution.
403498 region : defaultRegion ,
404499 } ,
405500 } )
406501 return async ( ) => {
407502 try {
408503 return await baseProvider ( )
409504 } catch ( error ) {
410- getLogger ( ) . error (
411- 'Console login authentication failed for profile %s in region %s: %O' ,
412- this . profileName ,
413- defaultRegion ,
414- error
415- )
416-
417- if (
418- error instanceof Error &&
419- ( error . message . includes ( 'Your session has expired' ) ||
420- error . message . includes ( 'Failed to load a token for session' ) ||
421- error . message . includes ( 'Failed to load token from' ) )
422- ) {
423- // Ask for user confirmation before refreshing
424- const response = await vscode . window . showInformationMessage (
425- `Unable to use your console credentials for profile "${ this . profileName } ". Would you like to refresh it?` ,
426- 'Refresh' ,
427- 'Cancel'
428- )
429-
430- if ( response !== 'Refresh' ) {
431- throw ToolkitError . chain ( error , 'User cancelled console credentials token refresh.' , {
432- code : 'LoginSessionRefreshCancelled' ,
433- cancelled : true ,
434- } )
435- }
436-
437- getLogger ( ) . info ( 'Re-authenticating using console credentials for profile %s' , this . profileName )
438- // Execute the console login command with the existing profile and region
439- try {
440- await vscode . commands . executeCommand (
441- 'aws.toolkit.auth.consoleLogin' ,
442- this . profileName ,
443- defaultRegion
444- )
445- } catch ( reAuthError ) {
446- throw ToolkitError . chain (
447- reAuthError ,
448- `Failed to refresh credentials for profile ${ this . profileName } . Run 'aws login --profile ${ this . profileName } ' to authenticate.` ,
449- { code : 'LoginSessionReAuthError' }
450- )
451- }
452-
453- getLogger ( ) . info (
454- 'Authentication completed for profile %s, refreshing credentials...' ,
455- this . profileName
456- )
457-
458- // Use the same provider instance but get fresh credentials
459- return await baseProvider ( )
505+ if ( error instanceof Error ) {
506+ await handleInvalidConsoleCredentials ( error , this . profileName , defaultRegion )
460507 }
461- throw ToolkitError . chain ( error , `Failed to get console credentials` , {
462- code : 'FromLoginCredentialProviderError' ,
463- } )
508+ throw error
464509 }
465510 }
466511 }
0 commit comments