diff --git a/clients/client-sts/defaultRoleAssumers.spec.ts b/clients/client-sts/defaultRoleAssumers.spec.ts new file mode 100644 index 000000000000..328e465283cc --- /dev/null +++ b/clients/client-sts/defaultRoleAssumers.spec.ts @@ -0,0 +1,62 @@ +import { HttpResponse } from "@aws-sdk/protocol-http"; +import { Readable } from "stream"; +const assumeRoleResponse = ` + + + AROAZOX2IL27GNRBJHWC2:session + arn:aws:sts::123:assumed-role/assume-role-test/session + + + key + secrete + session-token + 2021-05-05T23:22:08Z + + + + 12345678id + +`; +const mockHandle = jest.fn().mockResolvedValue({ + response: new HttpResponse({ + statusCode: 200, + body: Readable.from([""]), + }), +}); +jest.mock("@aws-sdk/node-http-handler", () => ({ + NodeHttpHandler: jest.fn().mockImplementation(() => ({ + destroy: () => {}, + handle: mockHandle, + })), + streamCollector: async () => Buffer.from(assumeRoleResponse), +})); + +import { getDefaultRoleAssumer } from "./defaultRoleAssumers"; +import type { AssumeRoleCommandInput } from "./commands/AssumeRoleCommand"; + +describe("getDefaultRoleAssumer", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it("should use supplied source credentials", async () => { + const roleAssumer = getDefaultRoleAssumer(); + const params: AssumeRoleCommandInput = { + RoleArn: "arn:aws:foo", + RoleSessionName: "session", + }; + const sourceCred1 = { accessKeyId: "key1", secretAccessKey: "secrete1" }; + await roleAssumer(sourceCred1, params); + expect(mockHandle).toBeCalledTimes(1); + // Validate request is signed by sourceCred1 + expect(mockHandle.mock.calls[0][0].headers?.authorization).toEqual( + expect.stringContaining("AWS4-HMAC-SHA256 Credential=key1/") + ); + const sourceCred2 = { accessKeyId: "key2", secretAccessKey: "secrete1" }; + await roleAssumer(sourceCred2, params); + // Validate request is signed by sourceCred2 + expect(mockHandle).toBeCalledTimes(2); + expect(mockHandle.mock.calls[1][0].headers?.authorization).toEqual( + expect.stringContaining("AWS4-HMAC-SHA256 Credential=key2/") + ); + }); +}); diff --git a/clients/client-sts/defaultStsRoleAssumers.ts b/clients/client-sts/defaultStsRoleAssumers.ts index a29b41b6cd0b..638ef7a9d9b7 100644 --- a/clients/client-sts/defaultStsRoleAssumers.ts +++ b/clients/client-sts/defaultStsRoleAssumers.ts @@ -39,12 +39,15 @@ export const getDefaultRoleAssumer = ( stsClientCtor: new (options: STSClientConfig) => STSClient ): RoleAssumer => { let stsClient: STSClient; + let closureSourceCreds: Credentials; return async (sourceCreds, params) => { + closureSourceCreds = sourceCreds; if (!stsClient) { const { logger, region } = stsOptions; stsClient = new stsClientCtor({ logger, - credentials: sourceCreds, + // A hack to make sts client uses the credential in current closure. + credentialDefaultProvider: () => async () => closureSourceCreds, region: decorateDefaultRegion(region), }); } diff --git a/packages/credential-provider-node/src/index.ts b/packages/credential-provider-node/src/index.ts index 6486db93b508..3c123ffd0ccb 100644 --- a/packages/credential-provider-node/src/index.ts +++ b/packages/credential-provider-node/src/index.ts @@ -55,7 +55,13 @@ export const defaultProvider = ( ): CredentialProvider => { const options = { profile: process.env[ENV_PROFILE], ...init }; if (!options.loadedConfig) options.loadedConfig = loadSharedConfigFiles(init); - const providers = [fromSSO(options), fromIni(options), fromProcess(options), fromTokenFile(options), remoteProvider(options)]; + const providers = [ + fromSSO(options), + fromIni(options), + fromProcess(options), + fromTokenFile(options), + remoteProvider(options), + ]; if (!options.profile) providers.unshift(fromEnv()); const providerChain = chain(...providers);