@@ -59,6 +59,25 @@ export interface AwsSdkSigV4AuthInputConfig {
59
59
signerConstructor ?: new ( options : SignatureV4Init & SignatureV4CryptoInit ) => RequestSigner ;
60
60
}
61
61
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
+
62
81
/**
63
82
* @internal
64
83
*/
@@ -82,7 +101,8 @@ export interface AwsSdkSigV4AuthResolvedConfig {
82
101
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.credentials}
83
102
* This provider MAY memoize the loaded credentials for certain period.
84
103
*/
85
- credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > ;
104
+ credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > &
105
+ AwsSdkSigV4Memoized ;
86
106
/**
87
107
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.signer}
88
108
*/
@@ -103,33 +123,42 @@ export interface AwsSdkSigV4AuthResolvedConfig {
103
123
export const resolveAwsSdkSigV4Config = < T > (
104
124
config : T & AwsSdkSigV4AuthInputConfig & AwsSdkSigV4PreviouslyResolved
105
125
) : 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
+ } ) ;
130
159
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 ;
133
162
134
163
// Populate sigv4 arguments
135
164
const {
@@ -172,7 +201,7 @@ export const resolveAwsSdkSigV4Config = <T>(
172
201
173
202
const params : SignatureV4Init & SignatureV4CryptoInit = {
174
203
...config ,
175
- credentials : boundCredentialsProvider ,
204
+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
176
205
region : config . signingRegion ,
177
206
service : config . signingName ,
178
207
sha256,
@@ -208,7 +237,7 @@ export const resolveAwsSdkSigV4Config = <T>(
208
237
209
238
const params : SignatureV4Init & SignatureV4CryptoInit = {
210
239
...config ,
211
- credentials : boundCredentialsProvider ,
240
+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
212
241
region : config . signingRegion ,
213
242
service : config . signingName ,
214
243
sha256,
@@ -220,17 +249,16 @@ export const resolveAwsSdkSigV4Config = <T>(
220
249
} ;
221
250
}
222
251
223
- return Object . assign ( config , {
252
+ const resolvedConfig = Object . assign ( config , {
224
253
systemClockOffset,
225
254
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 ! ,
232
255
signer,
233
256
} ) ;
257
+
258
+ return resolvedConfig as typeof resolvedConfig & {
259
+ // this was set earlier with Object.defineProperty.
260
+ credentials : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ;
261
+ } ;
234
262
} ;
235
263
236
264
/**
@@ -256,3 +284,63 @@ export interface AWSSDKSigV4AuthResolvedConfig extends AwsSdkSigV4AuthResolvedCo
256
284
* @deprecated renamed to {@link resolveAwsSdkSigV4Config}
257
285
*/
258
286
export 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