@@ -18,14 +18,14 @@ import { Commands } from '../shared/vscode/commands2'
18
18
import { createQuickPick , DataQuickPickItem , showQuickPick } from '../shared/ui/pickerPrompter'
19
19
import { isValidResponse } from '../shared/wizards/wizard'
20
20
import { CancellationError , Timeout } from '../shared/utilities/timeoutUtils'
21
- import { errorCode , formatError , ToolkitError , UnknownError } from '../shared/errors'
21
+ import { errorCode , formatError , isAwsError , ToolkitError , UnknownError } from '../shared/errors'
22
22
import { getCache } from './sso/cache'
23
23
import { createFactoryFunction , isNonNullable , Mutable } from '../shared/utilities/tsUtils'
24
24
import { builderIdStartUrl , SsoToken } from './sso/model'
25
25
import { SsoClient } from './sso/clients'
26
26
import { getLogger } from '../shared/logger'
27
27
import { CredentialsProviderManager } from './providers/credentialsProviderManager'
28
- import { asString , CredentialsProvider , fromString } from './providers/credentials'
28
+ import { asString , CredentialsId , CredentialsProvider , fromString } from './providers/credentials'
29
29
import { once } from '../shared/utilities/functionUtils'
30
30
import { getResourceFromTreeNode } from '../shared/treeview/utils'
31
31
import { Instance } from '../shared/utilities/typeConstructors'
@@ -46,8 +46,9 @@ import { AsyncCollection, toCollection } from '../shared/utilities/asyncCollecti
46
46
import { join , toStream } from '../shared/utilities/collectionUtils'
47
47
import { getConfigFilename } from './sharedCredentialsFile'
48
48
import { saveProfileToCredentials } from './sharedCredentials'
49
- import { SectionName , StaticCredentialsProfileKeys } from './types'
49
+ import { SectionName , SharedCredentialsKeys , StaticProfile , StaticProfileKeyErrorMessage } from './types'
50
50
import { throwOnInvalidCredentials } from './sharedCredentialsValidation'
51
+ import { TempCredentialProvider } from './providers/tempCredentialsProvider'
51
52
52
53
export const ssoScope = 'sso:account:access'
53
54
export const codecatalystScopes = [ 'codecatalyst:read_write' ]
@@ -586,6 +587,54 @@ export class Auth implements AuthService, ConnectionManager {
586
587
return this . #validationErrors. get ( connection . id )
587
588
}
588
589
590
+ /**
591
+ * Authenticates the given data and returns error info if it fails.
592
+ *
593
+ * @returns undefined if authentication succeeds, otherwise object with error info
594
+ */
595
+ public async authenticateData ( data : StaticProfile ) : Promise < StaticProfileKeyErrorMessage | undefined > {
596
+ const tempId = await this . addTempCredential ( data )
597
+ const tempIdString = asString ( tempId )
598
+ try {
599
+ await this . reauthenticate ( { id : tempIdString } )
600
+ } catch ( e ) {
601
+ if ( isAwsError ( e ) ) {
602
+ if ( e . code === 'InvalidClientTokenId' ) {
603
+ return { key : SharedCredentialsKeys . AWS_ACCESS_KEY_ID , error : 'Invalid access key' }
604
+ } else if ( e . code === 'SignatureDoesNotMatch' ) {
605
+ return { key : SharedCredentialsKeys . AWS_SECRET_ACCESS_KEY , error : 'Invalid secret key' }
606
+ }
607
+ }
608
+ throw e
609
+ } finally {
610
+ await this . removeTempCredential ( tempId )
611
+ }
612
+ return undefined
613
+ }
614
+
615
+ private async addTempCredential ( data : StaticProfile ) : Promise < CredentialsId > {
616
+ const tempProvider = new TempCredentialProvider ( data )
617
+ this . iamProfileProvider . addProvider ( tempProvider )
618
+ await this . thrownOnConn ( tempProvider . getCredentialsId ( ) , 'not-exists' )
619
+ return tempProvider . getCredentialsId ( )
620
+ }
621
+ private async removeTempCredential ( id : CredentialsId ) {
622
+ this . iamProfileProvider . removeProvider ( id )
623
+ await this . thrownOnConn ( id , 'exists' )
624
+ }
625
+
626
+ private async thrownOnConn ( id : CredentialsId , throwOn : 'exists' | 'not-exists' ) {
627
+ const idAsString = asString ( id )
628
+ const conns = await this . listConnections ( ) // triggers loading of profile in to store
629
+ const connExists = conns . some ( conn => conn . id === idAsString )
630
+
631
+ if ( throwOn === 'exists' && connExists ) {
632
+ throw new ToolkitError ( `Conn should not exist: ${ idAsString } ` )
633
+ } else if ( throwOn === 'not-exists' && ! connExists ) {
634
+ throw new ToolkitError ( `Conn should exist: ${ idAsString } ` )
635
+ }
636
+ }
637
+
589
638
/**
590
639
* Attempts to remove all auth state related to the connection.
591
640
*
@@ -1204,16 +1253,33 @@ const addConnection = Commands.register({ id: 'aws.auth.addConnection', telemetr
1204
1253
1205
1254
export async function tryAddCredentials (
1206
1255
profileName : SectionName ,
1207
- profileData : StaticCredentialsProfileKeys ,
1256
+ profileData : StaticProfile ,
1208
1257
tryConnect = true
1209
1258
) : Promise < boolean > {
1259
+ const auth = Auth . instance
1260
+
1261
+ // sanity checks
1210
1262
await throwOnInvalidCredentials ( profileName , profileData )
1263
+ const authenticationError = await auth . authenticateData ( profileData )
1264
+ if ( authenticationError ) {
1265
+ throw new ToolkitError ( `Found error with '${ authenticationError . key } ':'${ authenticationError . error } ' ` , {
1266
+ code : 'InvalidCredentials' ,
1267
+ } )
1268
+ }
1269
+
1211
1270
await saveProfileToCredentials ( profileName , profileData )
1271
+
1212
1272
if ( tryConnect ) {
1213
- const auth = Auth . instance
1214
- const conn = await auth . getConnection ( { id : profileName } )
1273
+ const id = asString ( {
1274
+ credentialSource : 'profile' ,
1275
+ credentialTypeId : profileName ,
1276
+ } )
1277
+ const conn = await auth . getConnection ( { id } )
1278
+
1215
1279
if ( conn === undefined ) {
1216
- throw new ToolkitError ( 'Failed to get connection from profile' , { code : 'MissingConnection' } )
1280
+ throw new ToolkitError ( `Failed to get connection from profile: ${ profileName } ` , {
1281
+ code : 'MissingConnection' ,
1282
+ } )
1217
1283
}
1218
1284
1219
1285
await auth . useConnection ( conn )
0 commit comments