diff --git a/packages/credential-provider-ini/src/fromIni.ts b/packages/credential-provider-ini/src/fromIni.ts index 08a8c2ae18cf..795a4aa7ed55 100644 --- a/packages/credential-provider-ini/src/fromIni.ts +++ b/packages/credential-provider-ini/src/fromIni.ts @@ -57,13 +57,13 @@ export interface FromIniInit extends SourceProfileInit, CredentialProviderOption */ export const fromIni = (_init: FromIniInit = {}): RegionalAwsCredentialIdentityProvider => - async (props = {}) => { + async ({ contextClientConfig } = {}) => { const init: FromIniInit = { ..._init, }; - if (props.contextClientConfig?.region) { + if (contextClientConfig?.region) { init.parentClientConfig = { - region: props.contextClientConfig.region, + region: contextClientConfig.region, ..._init.parentClientConfig, }; } diff --git a/packages/credential-provider-web-identity/src/fromWebToken.ts b/packages/credential-provider-web-identity/src/fromWebToken.ts index 880a27772b3c..1650113733ff 100644 --- a/packages/credential-provider-web-identity/src/fromWebToken.ts +++ b/packages/credential-provider-web-identity/src/fromWebToken.ts @@ -169,7 +169,7 @@ export const fromWebToken = { ...init.clientConfig, credentialProviderLogger: init.logger, - ...(awsIdentityProperties?.contextClientConfig?.region + ...(awsIdentityProperties?.contextClientConfig?.region || init.parentClientConfig ? { parentClientConfig: { region: awsIdentityProperties?.contextClientConfig?.region, diff --git a/packages/credential-providers/src/createCredentialChain.spec.ts b/packages/credential-providers/src/createCredentialChain.spec.ts index 08679a87b3ef..04f5fbb67962 100644 --- a/packages/credential-providers/src/createCredentialChain.spec.ts +++ b/packages/credential-providers/src/createCredentialChain.spec.ts @@ -1,3 +1,4 @@ +import { RegionalAwsCredentialIdentityProvider } from "@aws-sdk/types"; import { ProviderError } from "@smithy/property-provider"; import { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@smithy/types"; import { describe, expect, test as it } from "vitest"; @@ -79,4 +80,33 @@ describe(createCredentialChain.name, () => { "@aws-sdk/credential-providers - createCredentialChain(...).expireAfter(ms) may not be called with a duration lower than five minutes." ); }); + + it("is compatible with contextual-region-aware credential providers", async () => { + const provider: RegionalAwsCredentialIdentityProvider = async ({ contextClientConfig } = {}) => { + return { + accessKeyId: "", + secretAccessKey: "", + sessionToken: (await contextClientConfig?.region()) ?? "wrong_region", + }; + }; + const errorProvider = async () => { + throw new ProviderError("", { tryNextLink: true }); + }; + + const chain = createCredentialChain(errorProvider, provider); + + expect( + await chain({ + contextClientConfig: { + async region() { + return "ap-northeast-1"; + }, + }, + }) + ).toEqual({ + accessKeyId: "", + secretAccessKey: "", + sessionToken: "ap-northeast-1", + }); + }); }); diff --git a/packages/credential-providers/src/createCredentialChain.ts b/packages/credential-providers/src/createCredentialChain.ts index 857ce1b5345a..0c536436f1a8 100644 --- a/packages/credential-providers/src/createCredentialChain.ts +++ b/packages/credential-providers/src/createCredentialChain.ts @@ -1,4 +1,9 @@ -import { chain as propertyProviderChain } from "@smithy/property-provider"; +import type { + AwsIdentityProperties, + RegionalAwsCredentialIdentityProvider, + RegionalIdentityProvider, +} from "@aws-sdk/types"; +import { ProviderError } from "@smithy/property-provider"; import type { AwsCredentialIdentityProvider } from "@smithy/types"; export interface CustomCredentialChainOptions { @@ -52,11 +57,11 @@ type Mutable = { * providers in sequence until one succeeds or all fail. */ export const createCredentialChain = ( - ...credentialProviders: AwsCredentialIdentityProvider[] -): AwsCredentialIdentityProvider & CustomCredentialChainOptions => { + ...credentialProviders: RegionalAwsCredentialIdentityProvider[] +): RegionalAwsCredentialIdentityProvider & CustomCredentialChainOptions => { let expireAfter = -1; - const baseFunction = async () => { - const credentials = await propertyProviderChain(...credentialProviders)(); + const baseFunction = async (awsIdentityProperties?: AwsIdentityProperties) => { + const credentials = await propertyProviderChain(...credentialProviders)(awsIdentityProperties); if (!credentials.expiration && expireAfter !== -1) { (credentials as Mutable).expiration = new Date(Date.now() + expireAfter); } @@ -75,3 +80,29 @@ export const createCredentialChain = ( }); return withOptions; }; + +/** + * @internal + */ +export const propertyProviderChain = + (...providers: Array>): RegionalIdentityProvider => + async (awsIdentityProperties?: AwsIdentityProperties) => { + if (providers.length === 0) { + throw new ProviderError("No providers in chain"); + } + + let lastProviderError: Error | undefined; + for (const provider of providers) { + try { + const credentials = await provider(awsIdentityProperties); + return credentials; + } catch (err) { + lastProviderError = err; + if (err?.tryNextLink) { + continue; + } + throw err; + } + } + throw lastProviderError; + };