@@ -377,6 +377,84 @@ export class BitGoAPI implements BitGoBase {
377377 return this . _authVersion ;
378378 }
379379
380+ /**
381+ * Signs and sends a v2-authenticated request, then verifies the response HMAC.
382+ * Extracted from the req.then override in requestPatch to keep that method readable.
383+ */
384+ private async _sendRequestWithHmac ( {
385+ req,
386+ method,
387+ url,
388+ data,
389+ strategyAuthenticated,
390+ onfulfilled,
391+ originalThen,
392+ } : {
393+ req : superagent . SuperAgentRequest ;
394+ method : RequestMethods ;
395+ url : string ;
396+ data : string | undefined ;
397+ strategyAuthenticated : boolean ;
398+ onfulfilled : ( ( response : superagent . Response ) => any ) | null | undefined ;
399+ originalThen : ( onfulfilled : any , onrejected ?: any ) => Promise < any > ;
400+ } ) : Promise < any > {
401+ if ( this . _token || strategyAuthenticated ) {
402+ setRequestQueryString ( req ) ;
403+
404+ const requestProperties = await this . _hmacAuthStrategy . calculateRequestHeaders ( {
405+ url : req . url ,
406+ token : this . _token ?? '' ,
407+ method,
408+ text : data || '' ,
409+ authVersion : this . _authVersion ,
410+ } ) ;
411+ req . set ( 'Auth-Timestamp' , requestProperties . timestamp . toString ( ) ) ;
412+
413+ req . set ( 'Authorization' , 'Bearer ' + requestProperties . tokenHash ) ;
414+ debug (
415+ 'sending v2 %s request to %s with token %s' ,
416+ method ,
417+ url ,
418+ this . _token ?. substr ( 0 , 8 ) ?? '(strategy-managed)'
419+ ) ;
420+
421+ req . set ( 'HMAC' , requestProperties . hmac ) ;
422+ }
423+
424+ if ( this . getAdditionalHeadersCb ) {
425+ const additionalHeaders = this . getAdditionalHeadersCb ( method , url , data ) ;
426+ for ( const { key, value } of additionalHeaders ) {
427+ req . set ( key , value ) ;
428+ }
429+ }
430+
431+ /**
432+ * Verify the response before calling the original onfulfilled handler,
433+ * and make sure onrejected is called if a verification error is encountered
434+ */
435+ const newOnFulfilled = onfulfilled
436+ ? async ( response : superagent . Response ) => {
437+ // HMAC verification is only allowed to be skipped in certain environments.
438+ // This is checked in the constructor, but checking it again at request time
439+ // will help prevent against tampering of this property after the object is created
440+ if ( ! this . _hmacVerification && ! common . Environments [ this . getEnv ( ) ] . hmacVerificationEnforced ) {
441+ return onfulfilled ( response ) ;
442+ }
443+
444+ const verifiedResponse = await verifyResponseAsync (
445+ this ,
446+ this . _token ,
447+ method ,
448+ req ,
449+ response ,
450+ this . _authVersion
451+ ) ;
452+ return onfulfilled ( verifiedResponse ) ;
453+ }
454+ : null ;
455+ return originalThen ( newOnFulfilled ) ;
456+ }
457+
380458 /**
381459 * This is a patching function which can apply our authorization
382460 * headers to any outbound request.
@@ -446,65 +524,15 @@ export class BitGoAPI implements BitGoBase {
446524
447525 const data = serializeRequestData ( req ) ;
448526
449- const sendWithHmac = ( async ( ) => {
450- if ( this . _token || strategyAuthenticated ) {
451- setRequestQueryString ( req ) ;
452-
453- const requestProperties = await this . _hmacAuthStrategy . calculateRequestHeaders ( {
454- url : req . url ,
455- token : this . _token ?? '' ,
456- method,
457- text : data || '' ,
458- authVersion : this . _authVersion ,
459- } ) ;
460- req . set ( 'Auth-Timestamp' , requestProperties . timestamp . toString ( ) ) ;
461-
462- req . set ( 'Authorization' , 'Bearer ' + requestProperties . tokenHash ) ;
463- debug (
464- 'sending v2 %s request to %s with token %s' ,
465- method ,
466- url ,
467- this . _token ?. substr ( 0 , 8 ) ?? '(strategy-managed)'
468- ) ;
469-
470- req . set ( 'HMAC' , requestProperties . hmac ) ;
471- }
472-
473- if ( this . getAdditionalHeadersCb ) {
474- const additionalHeaders = this . getAdditionalHeadersCb ( method , url , data ) ;
475- for ( const { key, value } of additionalHeaders ) {
476- req . set ( key , value ) ;
477- }
478- }
479-
480- /**
481- * Verify the response before calling the original onfulfilled handler,
482- * and make sure onrejected is called if a verification error is encountered
483- */
484- const newOnFulfilled = onfulfilled
485- ? async ( response : superagent . Response ) => {
486- // HMAC verification is only allowed to be skipped in certain environments.
487- // This is checked in the constructor, but checking it again at request time
488- // will help prevent against tampering of this property after the object is created
489- if ( ! this . _hmacVerification && ! common . Environments [ this . getEnv ( ) ] . hmacVerificationEnforced ) {
490- return onfulfilled ( response ) ;
491- }
492-
493- const verifiedResponse = await verifyResponseAsync (
494- this ,
495- this . _token ,
496- method ,
497- req ,
498- response ,
499- this . _authVersion
500- ) ;
501- return onfulfilled ( verifiedResponse ) ;
502- }
503- : null ;
504- return originalThen ( newOnFulfilled ) ;
505- } ) ( ) ;
506-
507- return sendWithHmac . catch ( onrejected ) ;
527+ return this . _sendRequestWithHmac ( {
528+ req,
529+ method,
530+ url,
531+ data,
532+ strategyAuthenticated,
533+ onfulfilled,
534+ originalThen,
535+ } ) . catch ( onrejected ) ;
508536 } ;
509537 return toBitgoRequest ( req ) ;
510538 }
0 commit comments