@@ -1192,6 +1192,27 @@ async function handleNetworkV1EnterpriseClientConnections(
11921192 return handleProxyReq ( req , res , next ) ;
11931193}
11941194
1195+ /**
1196+ * Helper to send request body, using raw bytes when available.
1197+ * For v4 HMAC authentication, we need to send the exact bytes that were
1198+ * received from the client to ensure the HMAC signature matches.
1199+ *
1200+ * @param request - The superagent request object
1201+ * @param req - The Express request containing body and rawBodyBuffer
1202+ * @returns The request with body attached
1203+ */
1204+ function sendRequestBody ( request : ReturnType < BitGo [ 'post' ] > , req : express . Request ) {
1205+ if ( req . rawBodyBuffer ) {
1206+ const contentTypeHeader = req . headers [ 'content-type' ] ;
1207+ if ( contentTypeHeader ) {
1208+ request . set ( 'Content-Type' , Array . isArray ( contentTypeHeader ) ? contentTypeHeader [ 0 ] : contentTypeHeader ) ;
1209+ }
1210+ return request . send ( req . rawBodyBuffer . toString ( 'utf8' ) ) ;
1211+ }
1212+ // Fall back to parsed body for backward compatibility
1213+ return request . send ( req . body ) ;
1214+ }
1215+
11951216/**
11961217 * Redirect a request using the bitgo request functions.
11971218 * @param bitgo
@@ -1214,19 +1235,19 @@ export function redirectRequest(
12141235 request = bitgo . get ( url ) ;
12151236 break ;
12161237 case 'POST' :
1217- request = bitgo . post ( url ) . send ( req . body ) ;
1238+ request = sendRequestBody ( bitgo . post ( url ) , req ) ;
12181239 break ;
12191240 case 'PUT' :
1220- request = bitgo . put ( url ) . send ( req . body ) ;
1241+ request = sendRequestBody ( bitgo . put ( url ) , req ) ;
12211242 break ;
12221243 case 'PATCH' :
1223- request = bitgo . patch ( url ) . send ( req . body ) ;
1244+ request = sendRequestBody ( bitgo . patch ( url ) , req ) ;
12241245 break ;
12251246 case 'OPTIONS' :
1226- request = bitgo . options ( url ) . send ( req . body ) ;
1247+ request = sendRequestBody ( bitgo . options ( url ) , req ) ;
12271248 break ;
12281249 case 'DELETE' :
1229- request = bitgo . del ( url ) . send ( req . body ) ;
1250+ request = sendRequestBody ( bitgo . del ( url ) , req ) ;
12301251 break ;
12311252 }
12321253
@@ -1267,7 +1288,12 @@ function apiResponse(status: number, result: any, message?: string): ApiResponse
12671288 return new ApiResponseError ( message , status , result ) ;
12681289}
12691290
1270- const expressJSONParser = bodyParser . json ( { limit : '20mb' } ) ;
1291+ const expressJSONParser = bodyParser . json ( {
1292+ limit : '20mb' ,
1293+ verify : ( req , res , buf ) => {
1294+ ( req as express . Request ) . rawBodyBuffer = buf ;
1295+ } ,
1296+ } ) ;
12711297
12721298/**
12731299 * Perform body parsing here only on routes we want
0 commit comments