@@ -31,6 +31,15 @@ use aws_config::BehaviorVersion;
31
31
#[ cfg( feature = "aws-auth" ) ]
32
32
use aws_credential_types:: { provider:: ProvideCredentials , Credentials } ;
33
33
34
+ #[ cfg( feature = "aws-auth" ) ]
35
+ use aws_sigv4:: {
36
+ http_request:: { sign, SignableBody , SignableRequest , SigningSettings } ,
37
+ sign:: v4:: SigningParams ,
38
+ } ;
39
+
40
+ #[ cfg( feature = "aws-auth" ) ]
41
+ use http:: Request ;
42
+
34
43
const AWS_ECS_IP : & str = "169.254.170.2" ;
35
44
const AWS_EC2_IP : & str = "169.254.169.254" ;
36
45
const AWS_LONG_DATE_FMT : & str = "%Y%m%dT%H%M%SZ" ;
@@ -117,25 +126,32 @@ async fn authenticate_stream_inner(
117
126
let creds = get_aws_credentials ( credential) . await . map_err ( |e| {
118
127
Error :: authentication_error ( MECH_NAME , & format ! ( "failed to get creds: {e}" ) )
119
128
} ) ?;
120
- let aws_credential = AwsCredential :: from_sdk_creds ( creds) ;
121
129
122
130
let date = Utc :: now ( ) ;
123
131
124
- let authorization_header = aws_credential. compute_authorization_header (
132
+ // Generate authorization header using original implementation without AWS SDK
133
+ // let authorization_header = aws_credential.compute_authorization_header(
134
+ // date,
135
+ // &server_first.sts_host,
136
+ // &server_first.server_nonce,
137
+ // )?;
138
+
139
+ // let mut client_second_payload = doc! {
140
+ // "a": authorization_header,
141
+ // "d": date.format(AWS_LONG_DATE_FMT).to_string(),
142
+ // };
143
+
144
+ // if let Some(security_token) = aws_credential.session_token {
145
+ // client_second_payload.insert("t", security_token);
146
+ // }
147
+
148
+ let client_second_payload = compute_aws_sigv4_payload (
149
+ creds,
125
150
date,
126
151
& server_first. sts_host ,
127
152
& server_first. server_nonce ,
128
153
) ?;
129
154
130
- let mut client_second_payload = doc ! {
131
- "a" : authorization_header,
132
- "d" : date. format( AWS_LONG_DATE_FMT ) . to_string( ) ,
133
- } ;
134
-
135
- if let Some ( security_token) = aws_credential. session_token {
136
- client_second_payload. insert ( "t" , security_token) ;
137
- }
138
-
139
155
let mut client_second_payload_bytes = vec ! [ ] ;
140
156
client_second_payload. to_writer ( & mut client_second_payload_bytes) ?;
141
157
@@ -197,6 +213,119 @@ pub(crate) async fn get_aws_credentials(credential: &Credential) -> Result<Crede
197
213
}
198
214
}
199
215
216
+ pub fn compute_aws_sigv4_payload (
217
+ creds : Credentials ,
218
+ date : DateTime < Utc > ,
219
+ host : & str ,
220
+ server_nonce : & [ u8 ] ,
221
+ ) -> Result < Document > {
222
+ let region = if host == "sts.amazonaws.com" {
223
+ "us-east-1"
224
+ } else {
225
+ let parts: Vec < _ > = host. split ( '.' ) . collect ( ) ;
226
+ parts. get ( 1 ) . copied ( ) . unwrap_or ( "us-east-1" )
227
+ } ;
228
+
229
+ let url = format ! ( "https://{host}" ) ;
230
+ let date_str = date. format ( "%Y%m%dT%H%M%SZ" ) . to_string ( ) ;
231
+ let body_str = "Action=GetCallerIdentity&Version=2011-06-15" ;
232
+ let body_bytes = body_str. as_bytes ( ) ;
233
+ let nonce_b64 = base64:: encode ( server_nonce) ;
234
+
235
+ // Create the HTTP request
236
+ let mut builder = Request :: builder ( )
237
+ . method ( "POST" )
238
+ . uri ( & url)
239
+ . header ( "host" , host)
240
+ . header ( "content-type" , "application/x-www-form-urlencoded" )
241
+ . header ( "content-length" , body_bytes. len ( ) )
242
+ . header ( "x-amz-date" , & date_str)
243
+ . header ( "x-mongodb-gs2-cb-flag" , "n" )
244
+ . header ( "x-mongodb-server-nonce" , & nonce_b64) ;
245
+
246
+ if let Some ( token) = creds. session_token ( ) {
247
+ builder = builder. header ( "x-amz-security-token" , token) ;
248
+ }
249
+
250
+ let mut request = builder. body ( body_str. to_string ( ) ) . map_err ( |e| {
251
+ Error :: authentication_error ( MECH_NAME , & format ! ( "Failed to build request: {e}" ) )
252
+ } ) ?;
253
+
254
+ let service = "sts" ;
255
+ let identity = creds. into ( ) ;
256
+
257
+ // Set up signing parameters
258
+ let signing_settings = SigningSettings :: default ( ) ;
259
+ let signing_params = SigningParams :: builder ( )
260
+ . identity ( & identity)
261
+ . region ( region)
262
+ . name ( service)
263
+ . time ( date. into ( ) )
264
+ . settings ( signing_settings)
265
+ . build ( )
266
+ . map_err ( |e| {
267
+ Error :: authentication_error ( MECH_NAME , & format ! ( "Failed to build signing params: {e}" ) )
268
+ } ) ?
269
+ . into ( ) ;
270
+ let headers: Result < Vec < _ > > = request
271
+ . headers ( )
272
+ . iter ( )
273
+ . map ( |( k, v) | {
274
+ let v = v. to_str ( ) . map_err ( |_| {
275
+ Error :: authentication_error (
276
+ MECH_NAME ,
277
+ "Failed to convert header value to valid UTF-8" ,
278
+ )
279
+ } ) ?;
280
+ Ok ( ( k. as_str ( ) , v) )
281
+ } )
282
+ . collect ( ) ;
283
+
284
+ let signable_request = SignableRequest :: new (
285
+ request. method ( ) . as_str ( ) ,
286
+ request. uri ( ) . to_string ( ) ,
287
+ headers?. into_iter ( ) ,
288
+ SignableBody :: Bytes ( request. body ( ) . as_bytes ( ) ) ,
289
+ )
290
+ . map_err ( |e| {
291
+ Error :: authentication_error ( MECH_NAME , & format ! ( "Failed to create SignableRequest: {e}" ) )
292
+ } ) ?;
293
+
294
+ let ( signing_instructions, _signature) = sign ( signable_request, & signing_params)
295
+ . map_err ( |e| Error :: authentication_error ( MECH_NAME , & format ! ( "Signing failed: {e}" ) ) ) ?
296
+ . into_parts ( ) ;
297
+ signing_instructions. apply_to_request_http1x ( & mut request) ;
298
+
299
+ let headers = request. headers ( ) ;
300
+ let authorization_header = headers
301
+ . get ( "authorization" )
302
+ . ok_or_else ( || Error :: authentication_error ( MECH_NAME , "Missing authorization header" ) ) ?
303
+ . to_str ( )
304
+ . map_err ( |e| {
305
+ Error :: authentication_error ( MECH_NAME , & format ! ( "Invalid header value: {e}" ) )
306
+ } ) ?;
307
+
308
+ let token_header = headers
309
+ . get ( "x-amz-security-token" )
310
+ . map ( |v| {
311
+ v. to_str ( ) . map_err ( |e| {
312
+ Error :: authentication_error ( MECH_NAME , & format ! ( "Invalid token header: {e}" ) )
313
+ } )
314
+ } )
315
+ . transpose ( ) ?;
316
+
317
+ let mut payload = doc ! {
318
+ "a" : authorization_header,
319
+ "d" : date_str,
320
+ } ;
321
+
322
+ if let Some ( token) = token_header {
323
+ payload. insert ( "t" , token) ;
324
+ }
325
+
326
+ Ok ( payload)
327
+ }
328
+
200
329
/// Contains the credentials for MONGODB-AWS authentication.
201
330
// RUST-1529 note: dead_code tag added to avoid unused warnings on expiration field
202
331
#[ allow( dead_code) ]
0 commit comments