diff --git a/packages/nodes-base/credentials/common/aws/utils.test.ts b/packages/nodes-base/credentials/common/aws/utils.test.ts index 457c3861483..d53d4c0cd46 100644 --- a/packages/nodes-base/credentials/common/aws/utils.test.ts +++ b/packages/nodes-base/credentials/common/aws/utils.test.ts @@ -665,4 +665,63 @@ describe('awsGetSignInOptionsAndUpdateRequest', () => { ), ).toThrow(UserError); }); + + it('signs and sends UI Query Parameters supplied via qs (not only qs.query)', () => { + const { url, signOpts } = awsGetSignInOptionsAndUpdateRequest( + { + uri: 'https://bedrock.us-east-1.amazonaws.com/foundation-models', + qs: { byProvider: 'anthropic', byOutputModality: 'TEXT' }, + headers: {}, + } as any, + baseCredentials, + '', + 'GET', + 'bedrock', + 'us-east-1', + ); + + // Present in the outgoing URL... + const sentUrl = new URL(url); + expect(sentUrl.searchParams.get('byProvider')).toBe('anthropic'); + expect(sentUrl.searchParams.get('byOutputModality')).toBe('TEXT'); + // ...and in the signed path, so they are part of the SigV4 canonical request. + expect(signOpts.path).toContain('byProvider=anthropic'); + expect(signOpts.path).toContain('byOutputModality=TEXT'); + }); + + it('keeps query parameters embedded directly in the URL', () => { + const { url, signOpts } = awsGetSignInOptionsAndUpdateRequest( + { + uri: 'https://bedrock.us-east-1.amazonaws.com/foundation-models?byProvider=amazon', + headers: {}, + } as any, + baseCredentials, + '', + 'GET', + 'bedrock', + 'us-east-1', + ); + + expect(new URL(url).searchParams.get('byProvider')).toBe('amazon'); + expect(signOpts.path).toContain('byProvider=amazon'); + }); + + it('preserves the STS GetCallerIdentity special case', () => { + const { url } = awsGetSignInOptionsAndUpdateRequest( + { + uri: 'https://sts.us-east-1.amazonaws.com/', + qs: { Action: 'GetCallerIdentity' }, + headers: {}, + } as any, + baseCredentials, + '', + 'POST', + 'sts', + 'us-east-1', + ); + + const sentUrl = new URL(url); + expect(sentUrl.searchParams.get('Action')).toBe('GetCallerIdentity'); + expect(sentUrl.searchParams.get('Version')).toBe('2011-06-15'); + }); }); diff --git a/packages/nodes-base/credentials/common/aws/utils.ts b/packages/nodes-base/credentials/common/aws/utils.ts index b07fd6e1a29..c05da92c6e8 100644 --- a/packages/nodes-base/credentials/common/aws/utils.ts +++ b/packages/nodes-base/credentials/common/aws/utils.ts @@ -250,6 +250,10 @@ export function awsGetSignInOptionsAndUpdateRequest( } catch (err) { console.error(err); } + } else { + // UI Query Parameters are stored at the top level of requestOptions.qs, not under + // a nested `query` key, so merge the whole object to sign and send them. + query = requestWithUri.qs as IDataObject; } const parsed = parseAwsUrl(endpoint); service = parsed.service;