Skip to content

feat(auth): add IAM credentials LSP requests to AuthUtils and auth2 #7507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 74 commits into
base: feature/flare-mega
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
ab47a46
Add webviews for Amazon Q IAM credentials option and form
liramon1 Jun 5, 2025
eeebd0b
Implement IAM setup function
liramon1 Jun 6, 2025
61c1138
Make AuthUtils session switch between SsoLogin, IamLogin, and undefined
liramon1 Jun 9, 2025
146a95f
Start implementing IamLogin class
liramon1 Jun 10, 2025
eb0be34
Remove iamSessions from profiles
liramon1 Jun 11, 2025
a241b1c
Implement updateIamProfile and change STS references to IAM
liramon1 Jun 13, 2025
e923bad
Implement _getIamCredential and its callers
liramon1 Jun 13, 2025
2dabb43
fix(amazonq): delete iam profile when logout
yuxianrz Jun 16, 2025
ff181f4
start modifying auth2 consumers
liramon1 Jun 16, 2025
f61bcee
Merge remote-tracking branch 'origin/feature/flare-mega' into feature…
liramon1 Jun 16, 2025
eb3526a
undo unnecessary changes
liramon1 Jun 17, 2025
849006b
undo more unnecessary changes
liramon1 Jun 18, 2025
1968d41
fix logout bug
liramon1 Jun 18, 2025
06c5ad8
Fix bug where profile failed to be retrieved after signing out and ba…
liramon1 Jun 19, 2025
60ffc92
Fix region profile selector not triggering
liramon1 Jun 19, 2025
e1cfdc3
Add support for IAM credentials to region profile manager
liramon1 Jun 19, 2025
422d085
feat: remember iam access key
yuxianrz Jun 19, 2025
424a1af
Revert unnecessary region profile changes
liramon1 Jun 20, 2025
9dbe490
Add limited IAM support for inline chat
liramon1 Jun 20, 2025
f044eb6
Revert CredentialChangedKind for backwards compatibility
liramon1 Jun 20, 2025
7506ecc
fix: fix missing autofill after disabling extension
yuxianrz Jun 25, 2025
ce07bd2
Re-add session restore for IAM
liramon1 Jun 30, 2025
4d2b8b0
Add session token input to login flow
liramon1 Jul 1, 2025
5830716
Add session tokens to auth2 logic
liramon1 Jul 1, 2025
ef3745b
Replace profile deletion with iam invalidation
liramon1 Jul 1, 2025
1ec8508
Rename loginOnInvalidToken
liramon1 Jul 1, 2025
0382876
Rename getIamCredential options
liramon1 Jul 2, 2025
f45c92a
Remove assumeRole option
liramon1 Jul 2, 2025
b730331
feat: enable sts invalidation
yuxianrz Jul 2, 2025
6b5689a
Remove unused imports
liramon1 Jul 2, 2025
8ff6754
Add role ARN field to IAM credentials form
liramon1 Jul 3, 2025
e7f5d2a
feat: enable sts autorefresh
yuxianrz Jul 3, 2025
ee2f240
Update error codes in getIamCredential
liramon1 Jul 3, 2025
38d1389
Make secret key field a password input
liramon1 Jul 3, 2025
fd060c1
Autofill role arn field
liramon1 Jul 3, 2025
84f8310
fix: refreshed sts credentials can be used
yuxianrz Jul 3, 2025
1cd7745
Fix error statements
liramon1 Jul 7, 2025
dbbb4d6
Make errorNotification use show error instead of info message
liramon1 Jul 7, 2025
a4edd1f
Fix 'Connecting to IAM' UI bug
liramon1 Jul 7, 2025
423a73e
Revert empty profile
liramon1 Jul 9, 2025
c1dd5cb
fix unit tests
yuxianrz Jul 9, 2025
08b5856
add revert empty profile
yuxianrz Jul 9, 2025
654d96a
Split IamCredentialProfile into subprofiles
liramon1 Jul 10, 2025
09da3ff
refactor: prefix IAM-related profiles with 'Iam'
liramon1 Jul 10, 2025
5ace1fa
fix: remove unnecessary field invalidations when updating IAM profile
liramon1 Jul 11, 2025
f24cbe1
fix: invalidate fields for other IAM profile types
liramon1 Jul 11, 2025
a6d98d3
Add MFA code request handler
liramon1 Jul 14, 2025
7284196
add iam and sts unit tests
yuxianrz Jul 15, 2025
d8fdb25
Fix getMfaCode request handler types
liramon1 Jul 15, 2025
e71ac60
Merge branch 'feature/flare-iam' of github.com:liramon1/aws-toolkit-v…
liramon1 Jul 15, 2025
5acb15e
Naming changes
liramon1 Jul 15, 2025
77eb916
Use toolkit MFA prompt in Q
liramon1 Jul 15, 2025
8bf21d9
disable inline chat for iam login
yuxianrz Jul 16, 2025
ddf6866
fix: sync changes with iam-credentials PR
liramon1 Jul 17, 2025
7c920b1
Merge branch 'feature/flare-iam' of github.com:liramon1/aws-toolkit-v…
liramon1 Jul 17, 2025
01da622
revert unused change to connectionMetaData
yuxianrz Jul 17, 2025
2e4f529
fix auth2 unit test
yuxianrz Jul 17, 2025
6cbe195
fix chat-client-ui-types, runtimes and runtime types version
yuxianrz Jul 17, 2025
c78d9ee
Decouple profile name from iamCredentialId
liramon1 Jul 18, 2025
4a7a90a
Merge branch 'feature/flare-iam' of github.com:liramon1/aws-toolkit-v…
liramon1 Jul 18, 2025
aefd659
fix: fix unit test
liramon1 Jul 18, 2025
cefbb7c
Fix error message typo
liramon1 Jul 22, 2025
38dadca
fix eslint and update package.json
yuxianrz Jul 22, 2025
0a052ad
Merge branch 'feature/flare-iam' of github.com:liramon1/aws-toolkit-v…
yuxianrz Jul 22, 2025
7fe8fcc
emit telemetry for auth_iamOptionClick
yuxianrz Jul 23, 2025
5bfe0bf
fix eslint problems
yuxianrz Jul 23, 2025
dfcc0b0
fix emitIamClick according to new auth_signInOptionClcik
yuxianrz Jul 23, 2025
ef56385
add credentialType to iam telemetry metadata and fix emitIamClick
yuxianrz Jul 23, 2025
1b28bf8
send telemetry when attempts to use inline chat with IAM auth
yuxianrz Jul 24, 2025
c4acf81
change login_sso and login_iam, fix auth2.ts
yuxianrz Jul 24, 2025
9826566
fix incorrect connection message
yuxianrz Jul 24, 2025
0948746
feat: enable mfa serial input in pop up window
yuxianrz Jul 28, 2025
af80c9d
fix: wrong prompt for mfa serial input
yuxianrz Jul 29, 2025
6989096
fix: debug telemetry
yuxianrz Jul 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions aws-toolkit-vscode.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
{
"path": "packages/amazonq",
},
{
"path": "../language-servers",
},
{
"path": "../language-server-runtimes",
},
{
"path": "../aws-toolkit-common",
},
],
"settings": {
"typescript.tsdk": "node_modules/typescript/lib",
Expand Down
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions packages/amazonq/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"env": {
"SSMDOCUMENT_LANGUAGESERVER_PORT": "6010",
"WEBPACK_DEVELOPER_SERVER": "http://localhost:8080"
"WEBPACK_DEVELOPER_SERVER": "http://localhost:8080",
// Below allows for overrides used during development
// "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js",
// "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js"
"__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js",
"__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js"
},
"envFile": "${workspaceFolder}/.local.env",
"outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"],
Expand Down
7 changes: 5 additions & 2 deletions packages/amazonq/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ async function activateAmazonQNode(context: vscode.ExtensionContext) {
async function getAuthState(): Promise<Omit<AuthUserState, 'source'>> {
const state = AuthUtil.instance.getAuthState()

if (AuthUtil.instance.isConnected() && !(AuthUtil.instance.isSsoSession() || isSageMaker())) {
getLogger().error('Current Amazon Q connection is not SSO')
if (
AuthUtil.instance.isConnected() &&
!(AuthUtil.instance.isSsoSession() || AuthUtil.instance.isIamSession() || isSageMaker())
) {
getLogger().error('Current Amazon Q connection is not SSO nor IAM')
}

return {
Expand Down
55 changes: 53 additions & 2 deletions packages/amazonq/src/inlineChat/provider/inlineChatProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ChatTriggerType,
EditorContextExtractor,
PromptMessage,
TriggerEvent,
TriggerEventsStorage,
TriggerPayload,
triggerPayloadToChatRequest,
Expand All @@ -30,6 +31,7 @@ import { extractAuthFollowUp } from 'aws-core-vscode/amazonq'
import { InlineChatParams, InlineChatResult } from '@aws/language-server-runtimes-types'
import { decryptResponse, encryptRequest } from '../../lsp/encryption'
import { getCursorState } from '../../lsp/utils'
import { CwsprChatTriggerInteraction, telemetry } from 'aws-core-vscode/telemetry'

export class InlineChatProvider {
private readonly editorContextExtractor: EditorContextExtractor
Expand Down Expand Up @@ -68,7 +70,34 @@ export class InlineChatProvider {
}
}

private getTriggerInteractionFromTriggerEvent(triggerEvent: TriggerEvent | undefined): CwsprChatTriggerInteraction {
switch (triggerEvent?.type) {
case 'editor_context_command':
return triggerEvent.command?.triggerType === 'keybinding' ? 'hotkeys' : 'contextMenu'
case 'follow_up':
case 'chat_message':
default:
return 'click'
}
}

public async processPromptMessageLSP(message: PromptMessage): Promise<InlineChatResult> {
const triggerInteraction = this.getTriggerInteractionFromTriggerEvent(
this.triggerEventsStorage.getLastTriggerEventByTabID(message.tabID)
)
if (!AuthUtil.instance.isSsoSession()) {
telemetry.amazonq_messageResponseError.emit({
result: 'Failed',
cwsprChatConversationType: 'Chat',
cwsprChatRequestLength: message.message?.length ?? 0,
cwsprChatResponseCode: 401,
cwsprChatTriggerInteraction: triggerInteraction,
reason: 'AuthenticationError',
reasonDesc: 'Inline chat requires SSO authentication, but current session is not',
})
throw new ToolkitError('Inline chat is only available with SSO authentication')
}

// TODO: handle partial responses.
getLogger().info('Making inline chat request with message %O', message)
const params = this.getCurrentEditorParams(message.message ?? '')
Expand All @@ -83,6 +112,23 @@ export class InlineChatProvider {

// TODO: remove in favor of LSP implementation.
public async processPromptMessage(message: PromptMessage) {
const triggerInteraction = this.getTriggerInteractionFromTriggerEvent(
this.triggerEventsStorage.getLastTriggerEventByTabID(message.tabID)
)
if (!AuthUtil.instance.isSsoSession()) {
telemetry.amazonq_messageResponseError.emit({
result: 'Failed',
cwsprChatConversationType: 'Chat',
cwsprChatRequestLength: message.message?.length ?? 0,
cwsprChatResponseCode: 401,
cwsprChatTriggerInteraction: triggerInteraction,
reason: 'AuthenticationError',
reasonDesc: 'Inline chat requires SSO authentication, but current session is not',
credentialStartUrl: AuthUtil.instance.connection?.startUrl,
})
throw new ToolkitError('Inline chat is only available with SSO authentication')
}

return this.editorContextExtractor
.extractContextForTrigger('ChatMessage')
.then((context) => {
Expand Down Expand Up @@ -143,7 +189,7 @@ export class InlineChatProvider {
private async generateResponse(
triggerPayload: TriggerPayload & { projectContextQueryLatencyMs?: number },
triggerID: string
) {
): Promise<GenerateAssistantResponseCommandOutput | undefined> {
const triggerEvent = this.triggerEventsStorage.getTriggerEvent(triggerID)
if (triggerEvent === undefined) {
return
Expand Down Expand Up @@ -182,7 +228,12 @@ export class InlineChatProvider {
let response: GenerateAssistantResponseCommandOutput | undefined = undefined
session.createNewTokenSource()
try {
response = await session.chatSso(request)
if (AuthUtil.instance.isSsoSession()) {
response = await session.chatSso(request)
} else {
// Call sendMessage because Q Developer Streaming Client does not have generateAssistantResponse
throw new ToolkitError('Inline chat is only available with SSO authentication')
}
getLogger().info(
`response to tab: ${tabID} conversationID: ${session.sessionIdentifier} requestID: ${response.$metadata.requestId} metadata: %O`,
response.$metadata
Expand Down
28 changes: 26 additions & 2 deletions packages/amazonq/src/lsp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
ResponseError,
LSPErrorCodes,
updateConfigurationRequestType,
GetMfaCodeParams,
GetMfaCodeResult,
} from '@aws/language-server-runtimes/protocol'
import {
AuthUtil,
Expand All @@ -56,7 +58,7 @@ import { processUtils } from 'aws-core-vscode/shared'
import { activate as activateChat } from './chat/activation'
import { activate as activeInlineChat } from '../inlineChat/activation'
import { AmazonQResourcePaths } from './lspInstaller'
import { auth2 } from 'aws-core-vscode/auth'
import { auth2, getMfaTokenFromUser, getMfaSerialFromUser } from 'aws-core-vscode/auth'
import { ConfigSection, isValidConfigSection, pushConfigUpdate, toAmazonQLSPLogLevel } from './config'
import { telemetry } from 'aws-core-vscode/telemetry'
import { SessionManager } from '../app/inline/sessionManager'
Expand Down Expand Up @@ -164,6 +166,9 @@ export async function startLanguageServer(
},
credentials: {
providesBearerToken: true,
// Add IAM credentials support
providesIamCredentials: true,
supportsAssumeRole: true,
},
},
/**
Expand Down Expand Up @@ -211,9 +216,10 @@ export async function startLanguageServer(

/** All must be setup before {@link AuthUtil.restore} otherwise they may not trigger when expected */
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(async () => {
const activeProfile = AuthUtil.instance.regionProfileManager.activeRegionProfile
void pushConfigUpdate(client, {
type: 'profile',
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
profileArn: activeProfile?.arn,
})
})

Expand Down Expand Up @@ -333,6 +339,24 @@ async function postStartLanguageServer(
}
)

// Handler for when Flare needs to assume a role with MFA code
client.onRequest(
auth2.notificationTypes.getMfaCode.method,
async (params: GetMfaCodeParams): Promise<GetMfaCodeResult> => {
if (params.mfaSerial) {
globals.globalState.update('recentMfaSerial', { mfaSerial: params.mfaSerial })
}
const defaultMfaSerial = globals.globalState.tryGet('recentMfaSerial', Object, {
mfaSerial: '',
}).mfaSerial
let mfaSerial = await getMfaSerialFromUser(defaultMfaSerial, params.profileName)
mfaSerial = mfaSerial.trim()
globals.globalState.update('recentMfaSerial', { mfaSerial: mfaSerial })
const mfaCode = await getMfaTokenFromUser(mfaSerial, params.profileName)
return { code: mfaCode ?? '', mfaSerial: mfaSerial ?? '' }
}
)

const sendProfileToLsp = async () => {
try {
const result = await client.sendRequest(updateConfigurationRequestType.method, {
Expand Down
2 changes: 1 addition & 1 deletion packages/amazonq/test/e2e/amazonq/utils/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ export async function loginToIdC() {
)
}

await AuthUtil.instance.login(startUrl, region)
await AuthUtil.instance.loginSso(startUrl, region)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ describe('RegionProfileManager', async function () {

async function setupConnection(type: 'builderId' | 'idc') {
if (type === 'builderId') {
await AuthUtil.instance.login(constants.builderIdStartUrl, region)
await AuthUtil.instance.loginSso(constants.builderIdStartUrl, region)
assert.ok(AuthUtil.instance.isSsoSession())
assert.ok(AuthUtil.instance.isBuilderIdConnection())
} else if (type === 'idc') {
await AuthUtil.instance.login(enterpriseSsoStartUrl, region)
await AuthUtil.instance.loginSso(enterpriseSsoStartUrl, region)
assert.ok(AuthUtil.instance.isSsoSession())
assert.ok(AuthUtil.instance.isIdcConnection())
}
Expand Down
Loading
Loading