This article is a translation of https://zenn.dev/tilfin/articles/56f8dc56b83901.
Background
The AWS SDK for JavaScript v3 introduced a significant shift in client configuration compared to v2. While v2 allowed for global SDK settings, v3 adopts a per-client instance runtime configuration approach. Consequently, credential retrieval, which was internally cached in v2 even with randomly generated clients for each AWS resource, now presents potential challenges in v3 with frequent credential requests.
Delving into Credential Retrieval Logic
The simplest approach involves directly setting AWS access and secret keys as credentials
in the constructor arguments of AWS resource clients. However, this method is not ideal for real-world application development. Applications deployed on EC2 or ECS typically retrieve credentials from instance or container metadata services, while Lambda functions often obtain them via environment variables. The AWS SDK v3 implements these credential supply logics as Credential Providers.
You can also specify a Credential Provider for the client's credentials
. Details are described on the following page:
https://github.com/aws/aws-sdk-js-v3/tree/main/packages/credential-providers
If nothing is specified for credentials
, the function set in credentialDefaultProvider
is executed first to generate a Credential Provider, which is then used as credentials
. The default credentialDefaultProvider
is defaultProvider
. It attempts various acquisition methods. By using this method, you can automatically refer to ~/.aws/credentials
in a local environment and metadata in a deployed environment.
To explicitly specify the implementation of defaultProvider
, use fromNodeProviderChain
.
Credential Caching Mechanism
Credential retrieval occurs each time a client invokes a command. However, Credential Providers, which implement the various retrieval logics provided by the AWS SDK, do not have a caching mechanism. The only exception is the memoize
function of @smithy/property-provider
used internally by defaultProvider
. Furthermore, the re-acquisition logic for time-limited credentials, including session tokens, is also implemented only here.
What are the Best Practices?
Based on the insights so far, in most cases where you are implementing an application that operates within the same AWS account, "using defaultProvider
", or "specifying nothing", is the best approach. However, there is a potential pitfall here.
As mentioned at the beginning, in v3, since each client has a different Credential Provider, caching is also done on a per-client basis. For example, in a batch processing loop where the code "creates a client and retrieves an item", there is a possibility that credential acquisition will fail. Below is an issue where an error occurred because the access limit of EC2's metadata endpoint was reached.
https://github.com/aws/aws-sdk-js-v3/issues/4867
As a workaround, the issue's comments suggest caching (reusing) the instances of each AWS client themselves. However, in applications that rely on many AWS services and where command transmissions to them occur intensively, this may not be avoidable. There is also an improvement proposal for this.
https://github.com/aws/aws-sdk-js-v3/issues/4612
Conclusion
Currently, the best practice is to reuse defaultProvider
. Since fromNodeProviderChain
is provided by the SDK, generate it at the module level as shown below and reuse it.
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
export const credentialProvider = fromNodeProviderChain();
new S3Client({ credentials: credentialProvider });
new DynamoDBClient({ credentials: credentialProvider });
This will prevent the retrieval process from being concentrated internally.
Top comments (0)