@@ -2645,6 +2645,84 @@ describe('OAuth Authorization', () => {
26452645 expect ( authUrl . origin + authUrl . pathname ) . toBe ( 'https://auth.example.com/oauth/authorize' ) ;
26462646 } ) ;
26472647
2648+ it ( 'keeps the fallback MCP origin when legacy no-PRM auth metadata has an invalid issuer' , async ( ) => {
2649+ const saveDiscoveryState = vi . fn ( ) ;
2650+ const saveAuthorizationServerUrl = vi . fn ( ) ;
2651+ const provider : OAuthClientProvider = {
2652+ ...mockProvider ,
2653+ clientInformation : vi . fn ( ) . mockResolvedValue ( undefined ) ,
2654+ tokens : vi . fn ( ) . mockResolvedValue ( undefined ) ,
2655+ saveClientInformation : vi . fn ( ) ,
2656+ saveCodeVerifier : vi . fn ( ) ,
2657+ redirectToAuthorization : vi . fn ( ) ,
2658+ saveDiscoveryState,
2659+ saveAuthorizationServerUrl
2660+ } ;
2661+
2662+ mockFetch . mockImplementation ( url => {
2663+ const urlString = url . toString ( ) ;
2664+
2665+ if ( urlString . includes ( '/.well-known/oauth-protected-resource' ) ) {
2666+ return Promise . resolve ( {
2667+ ok : false ,
2668+ status : 404
2669+ } ) ;
2670+ }
2671+
2672+ if ( urlString === 'https://resource.example.com/.well-known/oauth-authorization-server' ) {
2673+ return Promise . resolve ( {
2674+ ok : true ,
2675+ status : 200 ,
2676+ json : async ( ) => ( {
2677+ issuer : 'auth.example.com' ,
2678+ authorization_endpoint : 'https://auth.example.com/oauth/authorize' ,
2679+ token_endpoint : 'https://auth.example.com/oauth/token' ,
2680+ registration_endpoint : 'https://auth.example.com/oauth/register' ,
2681+ response_types_supported : [ 'code' ] ,
2682+ code_challenge_methods_supported : [ 'S256' ]
2683+ } )
2684+ } ) ;
2685+ }
2686+
2687+ if ( urlString === 'https://auth.example.com/oauth/register' ) {
2688+ return Promise . resolve ( {
2689+ ok : true ,
2690+ status : 200 ,
2691+ json : async ( ) => ( {
2692+ client_id : 'test-client-id' ,
2693+ client_secret : 'test-client-secret' ,
2694+ client_id_issued_at : 1_612_137_600 ,
2695+ client_secret_expires_at : 1_612_224_000 ,
2696+ redirect_uris : [ 'http://localhost:3000/callback' ] ,
2697+ client_name : 'Test Client'
2698+ } )
2699+ } ) ;
2700+ }
2701+
2702+ return Promise . reject ( new Error ( `Unexpected fetch call: ${ urlString } ` ) ) ;
2703+ } ) ;
2704+
2705+ const result = await auth ( provider , {
2706+ serverUrl : 'https://resource.example.com'
2707+ } ) ;
2708+
2709+ expect ( result ) . toBe ( 'REDIRECT' ) ;
2710+ expect ( saveDiscoveryState ) . toHaveBeenCalledWith (
2711+ expect . objectContaining ( {
2712+ authorizationServerUrl : 'https://resource.example.com/' ,
2713+ resourceMetadata : undefined ,
2714+ authorizationServerMetadata : expect . objectContaining ( {
2715+ issuer : 'auth.example.com'
2716+ } )
2717+ } )
2718+ ) ;
2719+ expect ( saveAuthorizationServerUrl ) . toHaveBeenCalledWith ( 'https://resource.example.com/' ) ;
2720+
2721+ const redirectCall = ( provider . redirectToAuthorization as Mock ) . mock . calls [ 0 ] ! ;
2722+ const authUrl : URL = redirectCall [ 0 ] ;
2723+ expect ( authUrl . origin + authUrl . pathname ) . toBe ( 'https://auth.example.com/oauth/authorize' ) ;
2724+ } ) ;
2725+
26482726 it ( 'uses base URL (with root path) as authorization server when protected-resource-metadata discovery fails' , async ( ) => {
26492727 // Setup: First call to protected resource metadata fails (404)
26502728 // When no authorization_servers are found in protected resource metadata,
0 commit comments