aws / aws-sdk-js-v3

Modularized AWS SDK for JavaScript.
Apache License 2.0
3.02k stars 565 forks source link

Allow customizable credential provider chain #6272

Closed mxxk closed 2 weeks ago

mxxk commented 1 month ago

Describe the feature

Provide a construct to allow users of the AWS SDK for JavaScript v3 to express a chain of credential providers. An example of this is the @aws-sdk/credential-provider-node provider, but its chain of credentials is not customizable. This was possible in v2 using CredentialProviderChain, but there is no equivalent v3.

Use Case

Here are some example use cases which are not currently supported due to lack of custom composition of credential providers:

Proposed Solution

Since @aws-sdk/credential-provider-node relies on chain to compose multiple credential providers,

https://github.com/aws/aws-sdk-js-v3/blob/794a37e9795f390d15c9530209e465d099716c86/packages/credential-provider-node/src/defaultProvider.ts#L58-L60

one possible way forward might be to expose chain (and possibly memoize) for public consumption. Both currently belong to @smithy/property-provider, an internal package.

Other Information

No response

Acknowledgements

SDK version used

3.614.0

Environment details (OS name and version, etc.)

macOS 14.5

kuhe commented 1 month ago

You can use promises to create a credential chain.

import { fromEnv, fromSSO, fromIni, fromHttp } from "@aws-sdk/credential-providers";

const myCredentialProvider = async () => ({ secretAccessKey, ... })

new AWSSDKClient({
  credentials: () => 
    fromEnv()()
      .catch(fromSSO())
      .catch(fromHttp())
      .catch(fromIni())
      .catch(myCredentialProvider)
});
mxxk commented 1 month ago

Thanks @kuhe, if this is the best way, I can explore it further. One discrepancy I noticed right off the bat is that the .catch() in your example will catch all promise rejections, but chain only proceeds to the next provider if err?.tryNextLink property is set:

try {
  const credentials = await provider();
  return credentials;
} catch (err) {
  lastProviderError = err;
  if (err?.tryNextLink) {
    continue;
  }
  throw err;
}

Of course, this consideration can be rolled into a similar helper to chain, but wanted to call it out.

zshzbh commented 1 month ago

Hey @mxxk,

This tryNextLink is specifically for the error handling. Please refer to this part of code/comment

If a provider in the chain is rejected with an error, the chain will only proceed to the next provider if the value of the tryNextLink property on the error is truthy. This allows individual providers to halt the chain and also ensures the chain will stop if an entirely unexpected error is encountered.

It seems that you want us to export it, this was discussed by the team and the feature won’t be considered at this time unfortunately. I'd suggest to try the solution that @kuhe posted. And please don't hesitate to let us know if you have any questions!

Thanks! Maggie

mxxk commented 1 month ago

@zshzbh yep, that makes sense. Let me summarize your recommendation for this issue...

In order to create a custom credential provider chain, developers using AWS SDK for JavaScript v3 can implement a helper like the following:

function chainCredentialProviders(providers) {
  return async function getCredentials() {
    for (const provider of providers) {
      try {
        return await provider();
      } catch (error) {
        if (!error?.tryNextLink) {
          throw error;
        }
      }
    }
  };
}

This helper can be used as follows:

const myCustomCredentialProvider = async () => ({ secretAccessKey, ... });

const s3Client = new S3Client({
  region: "us-west-2",
  credentials: chainCredentialProviders([
    fromSSO(),
    fromHttp(),
    fromIni(),
    myCustomCredentialProvider,
  ]),
});

Since there is no additional action here, I can close this issue as "not planned", and hope the workaround documented here can help future readers.

github-actions[bot] commented 3 days ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.