@@ -1001,4 +1001,69 @@ describe("StreamableHTTPClientTransport", () => {
10011001      expect ( global . fetch ) . not . toHaveBeenCalled ( ) ; 
10021002    } ) ; 
10031003  } ) ; 
1004+ 
1005+   describe ( "prevent infinite recursion when server returns 401 after successful auth" ,  ( )  =>  { 
1006+     it ( "should throw error when server returns 401 after successful auth" ,  async  ( )  =>  { 
1007+     const  message : JSONRPCMessage  =  { 
1008+       jsonrpc : "2.0" , 
1009+       method : "test" , 
1010+       params : { } , 
1011+       id : "test-id" 
1012+     } ; 
1013+ 
1014+     // Mock provider with refresh token to enable token refresh flow 
1015+     mockAuthProvider . tokens . mockResolvedValue ( { 
1016+       access_token : "test-token" , 
1017+       token_type : "Bearer" , 
1018+       refresh_token : "refresh-token" , 
1019+     } ) ; 
1020+ 
1021+     const  unauthedResponse  =  { 
1022+       ok : false , 
1023+       status : 401 , 
1024+       statusText : "Unauthorized" , 
1025+       headers : new  Headers ( ) 
1026+     } ; 
1027+ 
1028+     ( global . fetch  as  jest . Mock ) 
1029+       // First request - 401, triggers auth flow 
1030+       . mockResolvedValueOnce ( unauthedResponse ) 
1031+       // Resource discovery, path aware 
1032+       . mockResolvedValueOnce ( unauthedResponse ) 
1033+       // Resource discovery, root 
1034+       . mockResolvedValueOnce ( unauthedResponse ) 
1035+       // OAuth metadata discovery 
1036+       . mockResolvedValueOnce ( { 
1037+         ok : true , 
1038+         status : 200 , 
1039+         json : async  ( )  =>  ( { 
1040+           issuer : "http://localhost:1234" , 
1041+           authorization_endpoint : "http://localhost:1234/authorize" , 
1042+           token_endpoint : "http://localhost:1234/token" , 
1043+           response_types_supported : [ "code" ] , 
1044+           code_challenge_methods_supported : [ "S256" ] , 
1045+         } ) , 
1046+       } ) 
1047+       // Token refresh succeeds 
1048+       . mockResolvedValueOnce ( { 
1049+         ok : true , 
1050+         status : 200 , 
1051+         json : async  ( )  =>  ( { 
1052+           access_token : "new-access-token" , 
1053+           token_type : "Bearer" , 
1054+           expires_in : 3600 , 
1055+         } ) , 
1056+       } ) 
1057+       // Retry the original request - still 401 (broken server) 
1058+       . mockResolvedValueOnce ( unauthedResponse ) ; 
1059+ 
1060+     await  expect ( transport . send ( message ) ) . rejects . toThrow ( "Server returned 401 after successful authentication" ) ; 
1061+     expect ( mockAuthProvider . saveTokens ) . toHaveBeenCalledWith ( { 
1062+       access_token : "new-access-token" , 
1063+       token_type : "Bearer" , 
1064+       expires_in : 3600 , 
1065+       refresh_token : "refresh-token" ,  // Refresh token is preserved 
1066+     } ) ; 
1067+   } ) ; 
1068+   } ) ; 
10041069} ) ; 
0 commit comments