@@ -2476,4 +2476,113 @@ describe('OAuth Authorization', () => {
24762476 expect ( body . get ( 'refresh_token' ) ) . toBe ( 'refresh123' ) ;
24772477 } ) ;
24782478 } ) ;
2479+
2480+ describe ( 'RequestInit headers passthrough' , ( ) => {
2481+ it ( 'custom headers from RequestInit are passed to auth discovery requests' , async ( ) => {
2482+ const { createFetchWithInit } = await import ( '../shared/transport.js' ) ;
2483+
2484+ const customFetch = jest . fn ( ) . mockResolvedValue ( {
2485+ ok : true ,
2486+ status : 200 ,
2487+ json : async ( ) => ( {
2488+ resource : 'https://resource.example.com' ,
2489+ authorization_servers : [ 'https://auth.example.com' ]
2490+ } )
2491+ } ) ;
2492+
2493+ // Create a wrapped fetch with custom headers
2494+ const wrappedFetch = createFetchWithInit ( customFetch , {
2495+ headers : {
2496+ 'user-agent' : 'MyApp/1.0' ,
2497+ 'x-custom-header' : 'test-value'
2498+ }
2499+ } ) ;
2500+
2501+ await discoverOAuthProtectedResourceMetadata ( 'https://resource.example.com' , undefined , wrappedFetch ) ;
2502+
2503+ expect ( customFetch ) . toHaveBeenCalledTimes ( 1 ) ;
2504+ const [ url , options ] = customFetch . mock . calls [ 0 ] ;
2505+
2506+ expect ( url . toString ( ) ) . toBe ( 'https://resource.example.com/.well-known/oauth-protected-resource' ) ;
2507+ expect ( options . headers ) . toMatchObject ( {
2508+ 'user-agent' : 'MyApp/1.0' ,
2509+ 'x-custom-header' : 'test-value' ,
2510+ 'MCP-Protocol-Version' : LATEST_PROTOCOL_VERSION
2511+ } ) ;
2512+ } ) ;
2513+
2514+ it ( 'auth-specific headers override base headers from RequestInit' , async ( ) => {
2515+ const { createFetchWithInit } = await import ( '../shared/transport.js' ) ;
2516+
2517+ const customFetch = jest . fn ( ) . mockResolvedValue ( {
2518+ ok : true ,
2519+ status : 200 ,
2520+ json : async ( ) => ( {
2521+ issuer : 'https://auth.example.com' ,
2522+ authorization_endpoint : 'https://auth.example.com/authorize' ,
2523+ token_endpoint : 'https://auth.example.com/token' ,
2524+ response_types_supported : [ 'code' ] ,
2525+ code_challenge_methods_supported : [ 'S256' ]
2526+ } )
2527+ } ) ;
2528+
2529+ // Create a wrapped fetch with a custom Accept header
2530+ const wrappedFetch = createFetchWithInit ( customFetch , {
2531+ headers : {
2532+ 'Accept' : 'text/plain' ,
2533+ 'user-agent' : 'MyApp/1.0'
2534+ }
2535+ } ) ;
2536+
2537+ await discoverAuthorizationServerMetadata ( 'https://auth.example.com' , {
2538+ fetchFn : wrappedFetch
2539+ } ) ;
2540+
2541+ expect ( customFetch ) . toHaveBeenCalled ( ) ;
2542+ const [ url , options ] = customFetch . mock . calls [ 0 ] ;
2543+
2544+ // Auth-specific Accept header should override base Accept header
2545+ expect ( options . headers ) . toMatchObject ( {
2546+ 'Accept' : 'application/json' , // Auth-specific value wins
2547+ 'user-agent' : 'MyApp/1.0' , // Base value preserved
2548+ 'MCP-Protocol-Version' : LATEST_PROTOCOL_VERSION
2549+ } ) ;
2550+ } ) ;
2551+
2552+ it ( 'other RequestInit options are passed through' , async ( ) => {
2553+ const { createFetchWithInit } = await import ( '../shared/transport.js' ) ;
2554+
2555+ const customFetch = jest . fn ( ) . mockResolvedValue ( {
2556+ ok : true ,
2557+ status : 200 ,
2558+ json : async ( ) => ( {
2559+ resource : 'https://resource.example.com' ,
2560+ authorization_servers : [ 'https://auth.example.com' ]
2561+ } )
2562+ } ) ;
2563+
2564+ // Create a wrapped fetch with various RequestInit options
2565+ const wrappedFetch = createFetchWithInit ( customFetch , {
2566+ credentials : 'include' ,
2567+ mode : 'cors' ,
2568+ cache : 'no-cache' ,
2569+ headers : {
2570+ 'user-agent' : 'MyApp/1.0'
2571+ }
2572+ } ) ;
2573+
2574+ await discoverOAuthProtectedResourceMetadata ( 'https://resource.example.com' , undefined , wrappedFetch ) ;
2575+
2576+ expect ( customFetch ) . toHaveBeenCalledTimes ( 1 ) ;
2577+ const [ url , options ] = customFetch . mock . calls [ 0 ] ;
2578+
2579+ // All RequestInit options should be preserved
2580+ expect ( options . credentials ) . toBe ( 'include' ) ;
2581+ expect ( options . mode ) . toBe ( 'cors' ) ;
2582+ expect ( options . cache ) . toBe ( 'no-cache' ) ;
2583+ expect ( options . headers ) . toMatchObject ( {
2584+ 'user-agent' : 'MyApp/1.0'
2585+ } ) ;
2586+ } ) ;
2587+ } ) ;
24792588} ) ;
0 commit comments