@@ -31,6 +31,15 @@ use aws_config::BehaviorVersion;
3131#[ cfg( feature = "aws-auth" ) ]
3232use aws_credential_types:: { provider:: ProvideCredentials , Credentials } ;
3333
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+
3443const AWS_ECS_IP : & str = "169.254.170.2" ;
3544const AWS_EC2_IP : & str = "169.254.169.254" ;
3645const AWS_LONG_DATE_FMT : & str = "%Y%m%dT%H%M%SZ" ;
@@ -117,25 +126,32 @@ async fn authenticate_stream_inner(
117126 let creds = get_aws_credentials ( credential) . await . map_err ( |e| {
118127 Error :: authentication_error ( MECH_NAME , & format ! ( "failed to get creds: {e}" ) )
119128 } ) ?;
120- let aws_credential = AwsCredential :: from_sdk_creds ( creds) ;
121129
122130 let date = Utc :: now ( ) ;
123131
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,
125150 date,
126151 & server_first. sts_host ,
127152 & server_first. server_nonce ,
128153 ) ?;
129154
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-
139155 let mut client_second_payload_bytes = vec ! [ ] ;
140156 client_second_payload. to_writer ( & mut client_second_payload_bytes) ?;
141157
@@ -197,6 +213,119 @@ pub(crate) async fn get_aws_credentials(credential: &Credential) -> Result<Crede
197213 }
198214}
199215
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+
200329/// Contains the credentials for MONGODB-AWS authentication.
201330// RUST-1529 note: dead_code tag added to avoid unused warnings on expiration field
202331#[ allow( dead_code) ]
0 commit comments