@@ -59,6 +59,21 @@ export interface AwsSdkSigV4AuthInputConfig {
5959 signerConstructor ?: new ( options : SignatureV4Init & SignatureV4CryptoInit ) => RequestSigner ;
6060}
6161
62+ /**
63+ * Used to indicate whether a credential provider function was memoized by this resolver.
64+ * @public
65+ */
66+ export type AwsSdkSigV4Memoized = {
67+ /**
68+ * The credential provider has been memoized by the AWS SDK SigV4 config resolver.
69+ */
70+ memoized ?: boolean ;
71+ /**
72+ * The credential provider has the caller client config object bound to its arguments.
73+ */
74+ configBound ?: boolean ;
75+ } ;
76+
6277/**
6378 * @internal
6479 */
@@ -82,7 +97,8 @@ export interface AwsSdkSigV4AuthResolvedConfig {
8297 * Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.credentials}
8398 * This provider MAY memoize the loaded credentials for certain period.
8499 */
85- credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > ;
100+ credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > &
101+ AwsSdkSigV4Memoized ;
86102 /**
87103 * Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.signer}
88104 */
@@ -103,33 +119,39 @@ export interface AwsSdkSigV4AuthResolvedConfig {
103119export const resolveAwsSdkSigV4Config = < T > (
104120 config : T & AwsSdkSigV4AuthInputConfig & AwsSdkSigV4PreviouslyResolved
105121) : T & AwsSdkSigV4AuthResolvedConfig => {
106- let isUserSupplied = false ;
107- // Normalize credentials
108- let credentialsProvider : AwsCredentialIdentityProvider | undefined ;
109- if ( config . credentials ) {
110- isUserSupplied = true ;
111- credentialsProvider = memoizeIdentityProvider ( config . credentials , isIdentityExpired , doesIdentityRequireRefresh ) ;
112- }
113- if ( ! credentialsProvider ) {
114- // credentialDefaultProvider should always be populated, but in case
115- // it isn't, set a default identity provider that throws an error
116- if ( config . credentialDefaultProvider ) {
117- credentialsProvider = normalizeProvider (
118- config . credentialDefaultProvider (
119- Object . assign ( { } , config as any , {
120- parentClientConfig : config ,
121- } )
122- )
123- ) ;
124- } else {
125- credentialsProvider = async ( ) => {
126- throw new Error ( "`credentials` is missing" ) ;
127- } ;
128- }
129- }
122+ let inputCredentials = config . credentials ;
123+ let isUserSupplied = ! ! config . credentials ;
124+ let resolvedCredentials : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] | undefined = undefined ;
125+
126+ Object . defineProperty ( config , "credentials" , {
127+ set ( credentials : AwsSdkSigV4AuthInputConfig [ "credentials" ] ) {
128+ if ( credentials && credentials !== inputCredentials && credentials !== resolvedCredentials ) {
129+ isUserSupplied = true ;
130+ }
131+ inputCredentials = credentials ;
132+ const memoizedProvider = normalizeCredentialProvider ( config , {
133+ credentials : inputCredentials ,
134+ credentialDefaultProvider : config . credentialDefaultProvider ,
135+ } ) ;
136+ const boundProvider = bindCallerConfig ( config , memoizedProvider ) ;
137+ if ( isUserSupplied ) {
138+ resolvedCredentials = async ( options : Record < string , any > | undefined ) =>
139+ boundProvider ( options ) . then ( ( creds : AttributedAwsCredentialIdentity ) =>
140+ setCredentialFeature ( creds , "CREDENTIALS_CODE" , "e" )
141+ ) ;
142+ } else {
143+ resolvedCredentials = boundProvider ;
144+ }
145+ } ,
146+ get ( ) : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] {
147+ return resolvedCredentials ! ;
148+ } ,
149+ enumerable : true ,
150+ configurable : true ,
151+ } ) ;
130152
131- const boundCredentialsProvider = async ( options : Record < string , any > | undefined ) =>
132- credentialsProvider ! ( { ... options , callerClientConfig : config } ) ;
153+ // invoke setter so that resolvedCredentials is set.
154+ config . credentials = inputCredentials ;
133155
134156 // Populate sigv4 arguments
135157 const {
@@ -172,7 +194,7 @@ export const resolveAwsSdkSigV4Config = <T>(
172194
173195 const params : SignatureV4Init & SignatureV4CryptoInit = {
174196 ...config ,
175- credentials : boundCredentialsProvider ,
197+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
176198 region : config . signingRegion ,
177199 service : config . signingName ,
178200 sha256,
@@ -208,7 +230,7 @@ export const resolveAwsSdkSigV4Config = <T>(
208230
209231 const params : SignatureV4Init & SignatureV4CryptoInit = {
210232 ...config ,
211- credentials : boundCredentialsProvider ,
233+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
212234 region : config . signingRegion ,
213235 service : config . signingName ,
214236 sha256,
@@ -220,17 +242,16 @@ export const resolveAwsSdkSigV4Config = <T>(
220242 } ;
221243 }
222244
223- return Object . assign ( config , {
245+ const resolvedConfig = Object . assign ( config , {
224246 systemClockOffset,
225247 signingEscapePath,
226- credentials : isUserSupplied
227- ? async ( options : Record < string , any > | undefined ) =>
228- boundCredentialsProvider ! ( options ) . then ( ( creds : AttributedAwsCredentialIdentity ) =>
229- setCredentialFeature ( creds , "CREDENTIALS_CODE" , "e" )
230- )
231- : boundCredentialsProvider ! ,
232248 signer,
233249 } ) ;
250+
251+ return resolvedConfig as typeof resolvedConfig & {
252+ // this was set earlier with Object.defineProperty.
253+ credentials : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ;
254+ } ;
234255} ;
235256
236257/**
@@ -256,3 +277,63 @@ export interface AWSSDKSigV4AuthResolvedConfig extends AwsSdkSigV4AuthResolvedCo
256277 * @deprecated renamed to {@link resolveAwsSdkSigV4Config}
257278 */
258279export const resolveAWSSDKSigV4Config = resolveAwsSdkSigV4Config ;
280+
281+ /**
282+ * Normalizes the credentials to a memoized provider and sets memoized=true on the function
283+ * object. This prevents multiple layering of the memoization process.
284+ */
285+ function normalizeCredentialProvider (
286+ config : Parameters < typeof resolveAwsSdkSigV4Config > [ 0 ] ,
287+ {
288+ credentials,
289+ credentialDefaultProvider,
290+ } : Pick < Parameters < typeof resolveAwsSdkSigV4Config > [ 0 ] , "credentials" | "credentialDefaultProvider" >
291+ ) : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] {
292+ let credentialsProvider : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] | undefined ;
293+
294+ if ( credentials ) {
295+ if ( ! ( credentials as typeof credentials & AwsSdkSigV4Memoized ) ?. memoized ) {
296+ credentialsProvider = memoizeIdentityProvider ( credentials , isIdentityExpired , doesIdentityRequireRefresh ) ! ;
297+ } else {
298+ credentialsProvider = credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ;
299+ }
300+ } else {
301+ // credentialDefaultProvider should always be populated, but in case
302+ // it isn't, set a default identity provider that throws an error
303+ if ( credentialDefaultProvider ) {
304+ credentialsProvider = normalizeProvider (
305+ credentialDefaultProvider (
306+ Object . assign ( { } , config as any , {
307+ parentClientConfig : config ,
308+ } )
309+ )
310+ ) ;
311+ } else {
312+ credentialsProvider = async ( ) => {
313+ throw new Error (
314+ "@aws-sdk/core::resolveAwsSdkSigV4Config - `credentials` not provided and no credentialDefaultProvider was configured."
315+ ) ;
316+ } ;
317+ }
318+ }
319+ credentialsProvider . memoized = true ;
320+ return credentialsProvider ;
321+ }
322+
323+ /**
324+ * Binds the caller client config as an argument to the credentialsProvider function.
325+ * Uses a state marker on the function to avoid doing this more than once.
326+ */
327+ function bindCallerConfig (
328+ config : Parameters < typeof resolveAwsSdkSigV4Config > [ 0 ] ,
329+ credentialsProvider : AwsSdkSigV4AuthResolvedConfig [ "credentials" ]
330+ ) : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] {
331+ if ( credentialsProvider . configBound ) {
332+ return credentialsProvider ;
333+ }
334+ const fn : typeof credentialsProvider = async ( options : Parameters < typeof credentialsProvider > [ 0 ] ) =>
335+ credentialsProvider ( { ...options , callerClientConfig : config } ) ;
336+ fn . memoized = credentialsProvider . memoized ;
337+ fn . configBound = true ;
338+ return fn ;
339+ }
0 commit comments