@@ -59,6 +59,25 @@ 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+ * Function is wrapped with attribution transform.
77+ */
78+ attributed ?: boolean ;
79+ } ;
80+
6281/**
6382 * @internal
6483 */
@@ -82,7 +101,8 @@ export interface AwsSdkSigV4AuthResolvedConfig {
82101 * Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.credentials}
83102 * This provider MAY memoize the loaded credentials for certain period.
84103 */
85- credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > ;
104+ credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > &
105+ AwsSdkSigV4Memoized ;
86106 /**
87107 * Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.signer}
88108 */
@@ -103,33 +123,42 @@ export interface AwsSdkSigV4AuthResolvedConfig {
103123export const resolveAwsSdkSigV4Config = < T > (
104124 config : T & AwsSdkSigV4AuthInputConfig & AwsSdkSigV4PreviouslyResolved
105125) : 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- }
126+ let inputCredentials = config . credentials ;
127+ let isUserSupplied = ! ! config . credentials ;
128+ let resolvedCredentials : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] | undefined = undefined ;
129+
130+ Object . defineProperty ( config , "credentials" , {
131+ set ( credentials : AwsSdkSigV4AuthInputConfig [ "credentials" ] ) {
132+ if ( credentials && credentials !== inputCredentials && credentials !== resolvedCredentials ) {
133+ isUserSupplied = true ;
134+ }
135+ inputCredentials = credentials ;
136+ const memoizedProvider = normalizeCredentialProvider ( config , {
137+ credentials : inputCredentials ,
138+ credentialDefaultProvider : config . credentialDefaultProvider ,
139+ } ) ;
140+ const boundProvider = bindCallerConfig ( config , memoizedProvider ) ;
141+ if ( isUserSupplied && ! boundProvider . attributed ) {
142+ resolvedCredentials = async ( options : Record < string , any > | undefined ) =>
143+ boundProvider ( options ) . then ( ( creds : AttributedAwsCredentialIdentity ) =>
144+ setCredentialFeature ( creds , "CREDENTIALS_CODE" , "e" )
145+ ) ;
146+ resolvedCredentials . memoized = boundProvider . memoized ;
147+ resolvedCredentials . configBound = boundProvider . configBound ;
148+ resolvedCredentials . attributed = true ;
149+ } else {
150+ resolvedCredentials = boundProvider ;
151+ }
152+ } ,
153+ get ( ) : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] {
154+ return resolvedCredentials ! ;
155+ } ,
156+ enumerable : true ,
157+ configurable : true ,
158+ } ) ;
130159
131- const boundCredentialsProvider = async ( options : Record < string , any > | undefined ) =>
132- credentialsProvider ! ( { ... options , callerClientConfig : config } ) ;
160+ // invoke setter so that resolvedCredentials is set.
161+ config . credentials = inputCredentials ;
133162
134163 // Populate sigv4 arguments
135164 const {
@@ -172,7 +201,7 @@ export const resolveAwsSdkSigV4Config = <T>(
172201
173202 const params : SignatureV4Init & SignatureV4CryptoInit = {
174203 ...config ,
175- credentials : boundCredentialsProvider ,
204+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
176205 region : config . signingRegion ,
177206 service : config . signingName ,
178207 sha256,
@@ -208,7 +237,7 @@ export const resolveAwsSdkSigV4Config = <T>(
208237
209238 const params : SignatureV4Init & SignatureV4CryptoInit = {
210239 ...config ,
211- credentials : boundCredentialsProvider ,
240+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
212241 region : config . signingRegion ,
213242 service : config . signingName ,
214243 sha256,
@@ -220,17 +249,16 @@ export const resolveAwsSdkSigV4Config = <T>(
220249 } ;
221250 }
222251
223- return Object . assign ( config , {
252+ const resolvedConfig = Object . assign ( config , {
224253 systemClockOffset,
225254 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 ! ,
232255 signer,
233256 } ) ;
257+
258+ return resolvedConfig as typeof resolvedConfig & {
259+ // this was set earlier with Object.defineProperty.
260+ credentials : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ;
261+ } ;
234262} ;
235263
236264/**
@@ -256,3 +284,63 @@ export interface AWSSDKSigV4AuthResolvedConfig extends AwsSdkSigV4AuthResolvedCo
256284 * @deprecated renamed to {@link resolveAwsSdkSigV4Config}
257285 */
258286export const resolveAWSSDKSigV4Config = resolveAwsSdkSigV4Config ;
287+
288+ /**
289+ * Normalizes the credentials to a memoized provider and sets memoized=true on the function
290+ * object. This prevents multiple layering of the memoization process.
291+ */
292+ function normalizeCredentialProvider (
293+ config : Parameters < typeof resolveAwsSdkSigV4Config > [ 0 ] ,
294+ {
295+ credentials,
296+ credentialDefaultProvider,
297+ } : Pick < Parameters < typeof resolveAwsSdkSigV4Config > [ 0 ] , "credentials" | "credentialDefaultProvider" >
298+ ) : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] {
299+ let credentialsProvider : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] | undefined ;
300+
301+ if ( credentials ) {
302+ if ( ! ( credentials as typeof credentials & AwsSdkSigV4Memoized ) ?. memoized ) {
303+ credentialsProvider = memoizeIdentityProvider ( credentials , isIdentityExpired , doesIdentityRequireRefresh ) ! ;
304+ } else {
305+ credentialsProvider = credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ;
306+ }
307+ } else {
308+ // credentialDefaultProvider should always be populated, but in case
309+ // it isn't, set a default identity provider that throws an error
310+ if ( credentialDefaultProvider ) {
311+ credentialsProvider = normalizeProvider (
312+ credentialDefaultProvider (
313+ Object . assign ( { } , config as any , {
314+ parentClientConfig : config ,
315+ } )
316+ )
317+ ) ;
318+ } else {
319+ credentialsProvider = async ( ) => {
320+ throw new Error (
321+ "@aws-sdk/core::resolveAwsSdkSigV4Config - `credentials` not provided and no credentialDefaultProvider was configured."
322+ ) ;
323+ } ;
324+ }
325+ }
326+ credentialsProvider . memoized = true ;
327+ return credentialsProvider ;
328+ }
329+
330+ /**
331+ * Binds the caller client config as an argument to the credentialsProvider function.
332+ * Uses a state marker on the function to avoid doing this more than once.
333+ */
334+ function bindCallerConfig (
335+ config : Parameters < typeof resolveAwsSdkSigV4Config > [ 0 ] ,
336+ credentialsProvider : AwsSdkSigV4AuthResolvedConfig [ "credentials" ]
337+ ) : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] {
338+ if ( credentialsProvider . configBound ) {
339+ return credentialsProvider ;
340+ }
341+ const fn : typeof credentialsProvider = async ( options : Parameters < typeof credentialsProvider > [ 0 ] ) =>
342+ credentialsProvider ( { ...options , callerClientConfig : config } ) ;
343+ fn . memoized = credentialsProvider . memoized ;
344+ fn . configBound = true ;
345+ return fn ;
346+ }
0 commit comments