Skip to content

Commit 2e0460a

Browse files
authored
fix(auth): more improvements (#2986)
## Problem * `invalidate` doesn't drop the cached registration * Users can switch to invalid static credentials * Outdated references to "SSO" ## Solution * Use LoginManager to validate profiles * Update `invalidate` * Change usages of "SSO" to relevant IAM Identity Center terminology
1 parent 9c52eef commit 2e0460a

File tree

6 files changed

+40
-19
lines changed

6 files changed

+40
-19
lines changed

src/credentials/auth.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ export class Auth implements AuthService, ConnectionManager {
434434

435435
return provider.invalidate()
436436
} else if (profile.type === 'iam') {
437-
globals.credentialsStore.invalidateCredentials(fromString(id))
437+
globals.loginManager.store.invalidateCredentials(fromString(id))
438438
}
439439
}
440440

@@ -518,7 +518,8 @@ export class Auth implements AuthService, ConnectionManager {
518518
profile: StoredProfile<SsoProfile>
519519
): SsoConnection & StatefulConnection {
520520
const provider = this.getTokenProvider(id, profile)
521-
const label = `SSO (${profile.startUrl})`
521+
const truncatedUrl = profile.startUrl.match(/https?:\/\/(.*)\.awsapps\.com\/start/)?.[1] ?? profile.startUrl
522+
const label = `IAM Identity Center (${truncatedUrl})`
522523

523524
return {
524525
id,
@@ -547,14 +548,15 @@ export class Auth implements AuthService, ConnectionManager {
547548

548549
private async createCachedCredentials(provider: CredentialsProvider) {
549550
const providerId = provider.getCredentialsId()
550-
globals.credentialsStore.invalidateCredentials(providerId)
551-
const { credentials } = await globals.credentialsStore.upsertCredentials(providerId, provider)
551+
globals.loginManager.store.invalidateCredentials(providerId)
552+
const { credentials } = await globals.loginManager.store.upsertCredentials(providerId, provider)
553+
await globals.loginManager.validateCredentials(credentials, provider.getDefaultRegion())
552554

553555
return credentials
554556
}
555557

556558
private async getCachedCredentials(provider: CredentialsProvider) {
557-
const creds = await globals.credentialsStore.getCredentials(provider.getCredentialsId())
559+
const creds = await globals.loginManager.store.getCredentials(provider.getCredentialsId())
558560
if (creds !== undefined && creds.credentialsHashCode === provider.getHashCode()) {
559561
return creds.credentials
560562
}
@@ -784,7 +786,7 @@ export async function createStartUrlPrompter(title: string) {
784786

785787
return createInputBox({
786788
title: `${title}: Enter Start URL`,
787-
placeholder: "Enter start URL for your organization's SSO",
789+
placeholder: "Enter start URL for your organization's AWS access portal",
788790
buttons: createCommonButtons(),
789791
validateInput: validateSsoUrl,
790792
})
@@ -807,7 +809,7 @@ const addConnection = Commands.register('aws.auth.addConnection', async () => {
807809
case 'iam':
808810
return await globals.awsContextCommands.onCommandCreateCredentialsProfile()
809811
case 'sso': {
810-
const startUrlPrompter = await createStartUrlPrompter('SSO Connection')
812+
const startUrlPrompter = await createStartUrlPrompter('IAM Identity Center')
811813
const startUrl = await startUrlPrompter.prompt()
812814
if (!isValidResponse(startUrl)) {
813815
throw new CancellationError('user')

src/credentials/loginManager.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class LoginManager {
4242

4343
public constructor(
4444
private readonly awsContext: AwsContext,
45-
private readonly store: CredentialsStore,
45+
public readonly store: CredentialsStore,
4646
public readonly recordAwsValidateCredentialsFn = telemetry.aws_validateCredentials.emit.bind(
4747
telemetry.aws_validateCredentials
4848
)
@@ -69,13 +69,7 @@ export class LoginManager {
6969
throw new Error(`No credentials found for id ${asString(args.providerId)}`)
7070
}
7171

72-
const credentialsRegion = provider.getDefaultRegion() ?? this.defaultCredentialsRegion
73-
const stsClient = new DefaultStsClient(credentialsRegion, credentials)
74-
const accountId = (await stsClient.getCallerIdentity()).Account
75-
if (!accountId) {
76-
throw new Error('Could not determine Account Id for credentials')
77-
}
78-
72+
const accountId = await this.validateCredentials(credentials, provider.getDefaultRegion())
7973
this.awsContext.credentialsShim = createCredentialsShim(this.store, args.providerId, credentials)
8074
await this.awsContext.setCredentials({
8175
credentials,
@@ -115,6 +109,16 @@ export class LoginManager {
115109
}
116110
}
117111

112+
public async validateCredentials(credentials: Credentials, region = this.defaultCredentialsRegion) {
113+
const stsClient = new DefaultStsClient(region, credentials)
114+
const accountId = (await stsClient.getCallerIdentity()).Account
115+
if (!accountId) {
116+
throw new Error('Could not determine Account Id for credentials')
117+
}
118+
119+
return accountId
120+
}
121+
118122
/**
119123
* Removes Credentials from the Toolkit. Essentially the Toolkit becomes "logged out".
120124
*

src/credentials/sso/ssoAccessTokenProvider.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ export class SsoAccessTokenProvider {
6262
) {}
6363

6464
public async invalidate(): Promise<void> {
65-
await this.cache.token.clear(this.tokenCacheKey)
65+
await Promise.all([
66+
this.cache.token.clear(this.tokenCacheKey),
67+
this.cache.registration.clear(this.registrationCacheKey),
68+
])
6669
}
6770

6871
public async getToken(): Promise<SsoToken | undefined> {

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export async function activate(context: vscode.ExtensionContext) {
112112

113113
await initializeAwsCredentialsStatusBarItem(awsContext, context)
114114
globals.regionProvider = regionProvider
115-
globals.credentialsStore = credentialsStore
115+
globals.loginManager = loginManager
116116
globals.awsContextCommands = new AwsContextCommands(regionProvider, Auth.instance)
117117
globals.sdkClientBuilder = new DefaultAWSClientBuilder(awsContext)
118118
globals.schemaService = new SchemaService(context)

src/shared/extensionGlobals.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { ExtensionContext, OutputChannel, Uri } from 'vscode'
7-
import { CredentialsStore } from '../credentials/credentialsStore'
7+
import { LoginManager } from '../credentials/loginManager'
88
import { AwsResourceManager } from '../dynamicResources/awsResourceManager'
99
import { AWSClientBuilder } from './awsClientBuilder'
1010
import { AwsContext } from './awsContext'
@@ -60,7 +60,7 @@ interface ToolkitGlobals {
6060
readonly window: Window
6161
// TODO: make the rest of these readonly (or delete them)
6262
outputChannel: OutputChannel
63-
credentialsStore: CredentialsStore
63+
loginManager: LoginManager
6464
awsContextCommands: AwsContextCommands
6565
awsContext: AwsContext
6666
regionProvider: RegionProvider

src/test/credentials/sso/ssoAccessTokenProvider.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ describe('SsoAccessTokenProvider', function () {
7878
await tryRemoveFolder(tempDir)
7979
})
8080

81+
describe('invalidate', function () {
82+
it('removes cached tokens and registrations', async function () {
83+
const validToken = createToken(HOUR_IN_MS)
84+
await cache.token.save(startUrl, { region, startUrl, token: validToken })
85+
await cache.registration.save({ region }, createRegistration(HOUR_IN_MS))
86+
await sut.invalidate()
87+
88+
assert.strictEqual(await cache.token.load(startUrl), undefined)
89+
assert.strictEqual(await cache.registration.load({ region }), undefined)
90+
})
91+
})
92+
8193
describe('getToken', function () {
8294
it('returns a cached token', async function () {
8395
const validToken = createToken(HOUR_IN_MS)

0 commit comments

Comments
 (0)