@@ -2558,4 +2558,113 @@ describe('OAuth Authorization', () => {
25582558 expect ( body . get ( 'refresh_token' ) ) . toBe ( 'refresh123' ) ;
25592559 } ) ;
25602560 } ) ;
2561+
2562+ describe ( 'RequestInit headers passthrough' , ( ) => {
2563+ it ( 'custom headers from RequestInit are passed to auth discovery requests' , async ( ) => {
2564+ const { createFetchWithInit } = await import ( '../shared/transport.js' ) ;
2565+
2566+ const customFetch = vi . fn ( ) . mockResolvedValue ( {
2567+ ok : true ,
2568+ status : 200 ,
2569+ json : async ( ) => ( {
2570+ resource : 'https://resource.example.com' ,
2571+ authorization_servers : [ 'https://auth.example.com' ]
2572+ } )
2573+ } ) ;
2574+
2575+ // Create a wrapped fetch with custom headers
2576+ const wrappedFetch = createFetchWithInit ( customFetch , {
2577+ headers : {
2578+ 'user-agent' : 'MyApp/1.0' ,
2579+ 'x-custom-header' : 'test-value'
2580+ }
2581+ } ) ;
2582+
2583+ await discoverOAuthProtectedResourceMetadata ( 'https://resource.example.com' , undefined , wrappedFetch ) ;
2584+
2585+ expect ( customFetch ) . toHaveBeenCalledTimes ( 1 ) ;
2586+ const [ url , options ] = customFetch . mock . calls [ 0 ] ;
2587+
2588+ expect ( url . toString ( ) ) . toBe ( 'https://resource.example.com/.well-known/oauth-protected-resource' ) ;
2589+ expect ( options . headers ) . toMatchObject ( {
2590+ 'user-agent' : 'MyApp/1.0' ,
2591+ 'x-custom-header' : 'test-value' ,
2592+ 'MCP-Protocol-Version' : LATEST_PROTOCOL_VERSION
2593+ } ) ;
2594+ } ) ;
2595+
2596+ it ( 'auth-specific headers override base headers from RequestInit' , async ( ) => {
2597+ const { createFetchWithInit } = await import ( '../shared/transport.js' ) ;
2598+
2599+ const customFetch = vi . fn ( ) . mockResolvedValue ( {
2600+ ok : true ,
2601+ status : 200 ,
2602+ json : async ( ) => ( {
2603+ issuer : 'https://auth.example.com' ,
2604+ authorization_endpoint : 'https://auth.example.com/authorize' ,
2605+ token_endpoint : 'https://auth.example.com/token' ,
2606+ response_types_supported : [ 'code' ] ,
2607+ code_challenge_methods_supported : [ 'S256' ]
2608+ } )
2609+ } ) ;
2610+
2611+ // Create a wrapped fetch with a custom Accept header
2612+ const wrappedFetch = createFetchWithInit ( customFetch , {
2613+ headers : {
2614+ Accept : 'text/plain' ,
2615+ 'user-agent' : 'MyApp/1.0'
2616+ }
2617+ } ) ;
2618+
2619+ await discoverAuthorizationServerMetadata ( 'https://auth.example.com' , {
2620+ fetchFn : wrappedFetch
2621+ } ) ;
2622+
2623+ expect ( customFetch ) . toHaveBeenCalled ( ) ;
2624+ const [ , options ] = customFetch . mock . calls [ 0 ] ;
2625+
2626+ // Auth-specific Accept header should override base Accept header
2627+ expect ( options . headers ) . toMatchObject ( {
2628+ Accept : 'application/json' , // Auth-specific value wins
2629+ 'user-agent' : 'MyApp/1.0' , // Base value preserved
2630+ 'MCP-Protocol-Version' : LATEST_PROTOCOL_VERSION
2631+ } ) ;
2632+ } ) ;
2633+
2634+ it ( 'other RequestInit options are passed through' , async ( ) => {
2635+ const { createFetchWithInit } = await import ( '../shared/transport.js' ) ;
2636+
2637+ const customFetch = vi . fn ( ) . mockResolvedValue ( {
2638+ ok : true ,
2639+ status : 200 ,
2640+ json : async ( ) => ( {
2641+ resource : 'https://resource.example.com' ,
2642+ authorization_servers : [ 'https://auth.example.com' ]
2643+ } )
2644+ } ) ;
2645+
2646+ // Create a wrapped fetch with various RequestInit options
2647+ const wrappedFetch = createFetchWithInit ( customFetch , {
2648+ credentials : 'include' ,
2649+ mode : 'cors' ,
2650+ cache : 'no-cache' ,
2651+ headers : {
2652+ 'user-agent' : 'MyApp/1.0'
2653+ }
2654+ } ) ;
2655+
2656+ await discoverOAuthProtectedResourceMetadata ( 'https://resource.example.com' , undefined , wrappedFetch ) ;
2657+
2658+ expect ( customFetch ) . toHaveBeenCalledTimes ( 1 ) ;
2659+ const [ , options ] = customFetch . mock . calls [ 0 ] ;
2660+
2661+ // All RequestInit options should be preserved
2662+ expect ( options . credentials ) . toBe ( 'include' ) ;
2663+ expect ( options . mode ) . toBe ( 'cors' ) ;
2664+ expect ( options . cache ) . toBe ( 'no-cache' ) ;
2665+ expect ( options . headers ) . toMatchObject ( {
2666+ 'user-agent' : 'MyApp/1.0'
2667+ } ) ;
2668+ } ) ;
2669+ } ) ;
25612670} ) ;
0 commit comments