@@ -59,6 +59,21 @@ 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
+
62
77
/**
63
78
* @internal
64
79
*/
@@ -82,7 +97,8 @@ export interface AwsSdkSigV4AuthResolvedConfig {
82
97
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.credentials}
83
98
* This provider MAY memoize the loaded credentials for certain period.
84
99
*/
85
- credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > ;
100
+ credentials : MergeFunctions < AwsCredentialIdentityProvider , MemoizedProvider < AwsCredentialIdentity > > &
101
+ AwsSdkSigV4Memoized ;
86
102
/**
87
103
* Resolved value for input config {@link AwsSdkSigV4AuthInputConfig.signer}
88
104
*/
@@ -103,33 +119,39 @@ export interface AwsSdkSigV4AuthResolvedConfig {
103
119
export const resolveAwsSdkSigV4Config = < T > (
104
120
config : T & AwsSdkSigV4AuthInputConfig & AwsSdkSigV4PreviouslyResolved
105
121
) : 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
+ } ) ;
130
152
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 ;
133
155
134
156
// Populate sigv4 arguments
135
157
const {
@@ -172,7 +194,7 @@ export const resolveAwsSdkSigV4Config = <T>(
172
194
173
195
const params : SignatureV4Init & SignatureV4CryptoInit = {
174
196
...config ,
175
- credentials : boundCredentialsProvider ,
197
+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
176
198
region : config . signingRegion ,
177
199
service : config . signingName ,
178
200
sha256,
@@ -208,7 +230,7 @@ export const resolveAwsSdkSigV4Config = <T>(
208
230
209
231
const params : SignatureV4Init & SignatureV4CryptoInit = {
210
232
...config ,
211
- credentials : boundCredentialsProvider ,
233
+ credentials : config . credentials as AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ,
212
234
region : config . signingRegion ,
213
235
service : config . signingName ,
214
236
sha256,
@@ -220,17 +242,16 @@ export const resolveAwsSdkSigV4Config = <T>(
220
242
} ;
221
243
}
222
244
223
- return Object . assign ( config , {
245
+ const resolvedConfig = Object . assign ( config , {
224
246
systemClockOffset,
225
247
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
248
signer,
233
249
} ) ;
250
+
251
+ return resolvedConfig as typeof resolvedConfig & {
252
+ // this was set earlier with Object.defineProperty.
253
+ credentials : AwsSdkSigV4AuthResolvedConfig [ "credentials" ] ;
254
+ } ;
234
255
} ;
235
256
236
257
/**
@@ -256,3 +277,63 @@ export interface AWSSDKSigV4AuthResolvedConfig extends AwsSdkSigV4AuthResolvedCo
256
277
* @deprecated renamed to {@link resolveAwsSdkSigV4Config}
257
278
*/
258
279
export 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