@@ -388,7 +388,7 @@ describe("OAuth Authorization", () => {
388
388
code_challenge_methods_supported : [ "S256" ] ,
389
389
} ;
390
390
391
- it ( "returns metadata when oauth-authorization-server discovery succeeds" , async ( ) => {
391
+ it ( "returns metadata when discovery succeeds" , async ( ) => {
392
392
mockFetch . mockResolvedValueOnce ( {
393
393
ok : true ,
394
394
status : 200 ,
@@ -406,28 +406,6 @@ describe("OAuth Authorization", () => {
406
406
} ) ;
407
407
} ) ;
408
408
409
- it ( "returns metadata when oidc discovery succeeds" , async ( ) => {
410
- mockFetch . mockImplementation ( ( url ) => {
411
- if ( url . toString ( ) . includes ( 'openid-configuration' ) ) {
412
- return Promise . resolve ( {
413
- ok : true ,
414
- status : 200 ,
415
- json : async ( ) => validMetadata ,
416
- } ) ;
417
- }
418
- return Promise . resolve ( {
419
- ok : false ,
420
- status : 404 ,
421
- } ) ;
422
- } ) ;
423
-
424
- const metadata = await discoverOAuthMetadata ( "https://auth.example.com" ) ;
425
- expect ( metadata ) . toEqual ( validMetadata ) ;
426
- expect ( mockFetch ) . toHaveBeenCalledTimes ( 2 ) ;
427
- expect ( mockFetch . mock . calls [ 0 ] [ 0 ] . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/oauth-authorization-server" ) ;
428
- expect ( mockFetch . mock . calls [ 1 ] [ 0 ] . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/openid-configuration" ) ;
429
- } ) ;
430
-
431
409
it ( "returns metadata when discovery succeeds with path" , async ( ) => {
432
410
mockFetch . mockResolvedValueOnce ( {
433
411
ok : true ,
@@ -446,14 +424,14 @@ describe("OAuth Authorization", () => {
446
424
} ) ;
447
425
} ) ;
448
426
449
- it ( "tries discovery endpoints in new spec order for URLs with path " , async ( ) => {
450
- // First call (OAuth with path insertion ) returns 404
427
+ it ( "falls back to root discovery when path-aware discovery returns 404 " , async ( ) => {
428
+ // First call (path-aware ) returns 404
451
429
mockFetch . mockResolvedValueOnce ( {
452
430
ok : false ,
453
431
status : 404 ,
454
432
} ) ;
455
433
456
- // Second call (OIDC with path insertion ) succeeds
434
+ // Second call (root fallback ) succeeds
457
435
mockFetch . mockResolvedValueOnce ( {
458
436
ok : true ,
459
437
status : 200 ,
@@ -466,35 +444,29 @@ describe("OAuth Authorization", () => {
466
444
const calls = mockFetch . mock . calls ;
467
445
expect ( calls . length ) . toBe ( 2 ) ;
468
446
469
- // First call should be OAuth with path insertion
447
+ // First call should be path-aware
470
448
const [ firstUrl , firstOptions ] = calls [ 0 ] ;
471
449
expect ( firstUrl . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/oauth-authorization-server/path/name" ) ;
472
450
expect ( firstOptions . headers ) . toEqual ( {
473
451
"MCP-Protocol-Version" : LATEST_PROTOCOL_VERSION
474
452
} ) ;
475
453
476
- // Second call should be OIDC with path insertion
454
+ // Second call should be root fallback
477
455
const [ secondUrl , secondOptions ] = calls [ 1 ] ;
478
- expect ( secondUrl . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/openid-configuration/path/name " ) ;
456
+ expect ( secondUrl . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/oauth-authorization-server " ) ;
479
457
expect ( secondOptions . headers ) . toEqual ( {
480
458
"MCP-Protocol-Version" : LATEST_PROTOCOL_VERSION
481
459
} ) ;
482
460
} ) ;
483
461
484
- it ( "returns undefined when all discovery endpoints return 404" , async ( ) => {
485
- // First call (OAuth with path insertion) returns 404
486
- mockFetch . mockResolvedValueOnce ( {
487
- ok : false ,
488
- status : 404 ,
489
- } ) ;
490
-
491
- // Second call (OIDC with path insertion) returns 404
462
+ it ( "returns undefined when both path-aware and root discovery return 404" , async ( ) => {
463
+ // First call (path-aware) returns 404
492
464
mockFetch . mockResolvedValueOnce ( {
493
465
ok : false ,
494
466
status : 404 ,
495
467
} ) ;
496
468
497
- // Third call (OIDC with path appending) returns 404
469
+ // Second call (root fallback) also returns 404
498
470
mockFetch . mockResolvedValueOnce ( {
499
471
ok : false ,
500
472
status : 404 ,
@@ -504,33 +476,7 @@ describe("OAuth Authorization", () => {
504
476
expect ( metadata ) . toBeUndefined ( ) ;
505
477
506
478
const calls = mockFetch . mock . calls ;
507
- expect ( calls . length ) . toBe ( 3 ) ;
508
- } ) ;
509
-
510
- it ( "tries all endpoints in correct order for URLs with path" , async ( ) => {
511
- // All calls return 404 to test the order
512
- mockFetch . mockResolvedValue ( {
513
- ok : false ,
514
- status : 404 ,
515
- } ) ;
516
-
517
- const metadata = await discoverOAuthMetadata ( "https://auth.example.com/tenant1" ) ;
518
- expect ( metadata ) . toBeUndefined ( ) ;
519
-
520
- const calls = mockFetch . mock . calls ;
521
- expect ( calls . length ) . toBe ( 3 ) ;
522
-
523
- // First call should be OAuth 2.0 Authorization Server Metadata with path insertion
524
- const [ firstUrl ] = calls [ 0 ] ;
525
- expect ( firstUrl . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/oauth-authorization-server/tenant1" ) ;
526
-
527
- // Second call should be OpenID Connect Discovery 1.0 with path insertion
528
- const [ secondUrl ] = calls [ 1 ] ;
529
- expect ( secondUrl . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/openid-configuration/tenant1" ) ;
530
-
531
- // Third call should be OpenID Connect Discovery 1.0 path appending
532
- const [ thirdUrl ] = calls [ 2 ] ;
533
- expect ( thirdUrl . toString ( ) ) . toBe ( "https://auth.example.com/tenant1/.well-known/openid-configuration" ) ;
479
+ expect ( calls . length ) . toBe ( 2 ) ;
534
480
} ) ;
535
481
536
482
it ( "does not fallback when the original URL is already at root path" , async ( ) => {
@@ -540,17 +486,11 @@ describe("OAuth Authorization", () => {
540
486
status : 404 ,
541
487
} ) ;
542
488
543
- // Second call (OIDC discovery) also returns 404
544
- mockFetch . mockResolvedValueOnce ( {
545
- ok : false ,
546
- status : 404 ,
547
- } ) ;
548
-
549
489
const metadata = await discoverOAuthMetadata ( "https://auth.example.com/" ) ;
550
490
expect ( metadata ) . toBeUndefined ( ) ;
551
491
552
492
const calls = mockFetch . mock . calls ;
553
- expect ( calls . length ) . toBe ( 2 ) ; // Should not attempt fallback but will try OIDC
493
+ expect ( calls . length ) . toBe ( 1 ) ; // Should not attempt fallback
554
494
555
495
const [ url ] = calls [ 0 ] ;
556
496
expect ( url . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/oauth-authorization-server" ) ;
@@ -563,42 +503,27 @@ describe("OAuth Authorization", () => {
563
503
status : 404 ,
564
504
} ) ;
565
505
566
- // Second call (OIDC discovery) also returns 404
567
- mockFetch . mockResolvedValueOnce ( {
568
- ok : false ,
569
- status : 404 ,
570
- } ) ;
571
-
572
506
const metadata = await discoverOAuthMetadata ( "https://auth.example.com" ) ;
573
507
expect ( metadata ) . toBeUndefined ( ) ;
574
508
575
509
const calls = mockFetch . mock . calls ;
576
- expect ( calls . length ) . toBe ( 2 ) ; // Should not attempt fallback but will try OIDC
510
+ expect ( calls . length ) . toBe ( 1 ) ; // Should not attempt fallback
577
511
578
512
const [ url ] = calls [ 0 ] ;
579
513
expect ( url . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/oauth-authorization-server" ) ;
580
514
} ) ;
581
515
582
- it ( "tries all endpoints when discovery encounters CORS error" , async ( ) => {
583
- // First call (OAuth with path insertion) fails with TypeError (CORS)
584
- mockFetch . mockImplementationOnce ( ( ) => Promise . reject ( new TypeError ( "CORS error" ) ) ) ;
585
-
586
- // Retry OAuth with path insertion without headers (simulating CORS retry)
587
- mockFetch . mockResolvedValueOnce ( {
588
- ok : false ,
589
- status : 404 ,
590
- } ) ;
591
-
592
- // Second call (OIDC with path insertion) fails with TypeError (CORS)
516
+ it ( "falls back when path-aware discovery encounters CORS error" , async ( ) => {
517
+ // First call (path-aware) fails with TypeError (CORS)
593
518
mockFetch . mockImplementationOnce ( ( ) => Promise . reject ( new TypeError ( "CORS error" ) ) ) ;
594
519
595
- // Retry OIDC with path insertion without headers (simulating CORS retry)
520
+ // Retry path-aware without headers (simulating CORS retry)
596
521
mockFetch . mockResolvedValueOnce ( {
597
522
ok : false ,
598
523
status : 404 ,
599
524
} ) ;
600
525
601
- // Third call (OIDC with path appending ) succeeds
526
+ // Second call (root fallback ) succeeds
602
527
mockFetch . mockResolvedValueOnce ( {
603
528
ok : true ,
604
529
status : 200 ,
@@ -609,11 +534,11 @@ describe("OAuth Authorization", () => {
609
534
expect ( metadata ) . toEqual ( validMetadata ) ;
610
535
611
536
const calls = mockFetch . mock . calls ;
612
- expect ( calls . length ) . toBe ( 5 ) ;
537
+ expect ( calls . length ) . toBe ( 3 ) ;
613
538
614
- // Final call should be OIDC with path appending
615
- const [ lastUrl , lastOptions ] = calls [ 4 ] ;
616
- expect ( lastUrl . toString ( ) ) . toBe ( "https://auth.example.com/deep/path/ .well-known/openid-configuration " ) ;
539
+ // Final call should be root fallback
540
+ const [ lastUrl , lastOptions ] = calls [ 2 ] ;
541
+ expect ( lastUrl . toString ( ) ) . toBe ( "https://auth.example.com/.well-known/oauth-authorization-server " ) ;
617
542
expect ( lastOptions . headers ) . toEqual ( {
618
543
"MCP-Protocol-Version" : LATEST_PROTOCOL_VERSION
619
544
} ) ;
@@ -691,14 +616,13 @@ describe("OAuth Authorization", () => {
691
616
} ) ;
692
617
693
618
it ( "returns undefined when discovery endpoint returns 404" , async ( ) => {
694
- mockFetch . mockResolvedValue ( {
619
+ mockFetch . mockResolvedValueOnce ( {
695
620
ok : false ,
696
621
status : 404 ,
697
622
} ) ;
698
623
699
624
const metadata = await discoverOAuthMetadata ( "https://auth.example.com" ) ;
700
625
expect ( metadata ) . toBeUndefined ( ) ;
701
- expect ( mockFetch ) . toHaveBeenCalledTimes ( 2 ) ;
702
626
} ) ;
703
627
704
628
it ( "throws on non-404 errors" , async ( ) => {
0 commit comments