1
+ import { mock , Mock } from 'node:test' ;
1
2
import { LATEST_PROTOCOL_VERSION } from '../types.js' ;
2
3
import {
3
4
discoverOAuthMetadata ,
@@ -16,8 +17,25 @@ const mockFetch = jest.fn();
16
17
global . fetch = mockFetch ;
17
18
18
19
describe ( "OAuth Authorization" , ( ) => {
20
+ let mockProvider : OAuthClientProvider ;
21
+
19
22
beforeEach ( ( ) => {
20
23
mockFetch . mockReset ( ) ;
24
+ mockProvider = {
25
+ get redirectUrl ( ) { return "http://localhost:3000/callback" ; } ,
26
+ get clientMetadata ( ) {
27
+ return {
28
+ redirect_uris : [ "http://localhost:3000/callback" ] ,
29
+ client_name : "Test Client" ,
30
+ } ;
31
+ } ,
32
+ clientInformation : jest . fn ( ) ,
33
+ tokens : jest . fn ( ) ,
34
+ saveTokens : jest . fn ( ) ,
35
+ redirectToAuthorization : jest . fn ( ) ,
36
+ saveCodeVerifier : jest . fn ( ) ,
37
+ codeVerifier ( ) { return "verifier123" ; } ,
38
+ } ;
21
39
} ) ;
22
40
23
41
describe ( "extractResourceMetadataUrl" , ( ) => {
@@ -462,9 +480,9 @@ describe("OAuth Authorization", () => {
462
480
{
463
481
metadata : undefined ,
464
482
clientInformation : validClientInfo ,
465
- redirectUrl : "http://localhost:3000/callback" ,
466
483
resource : new URL ( "https://api.example.com/mcp-server" ) ,
467
- }
484
+ } ,
485
+ mockProvider
468
486
) ;
469
487
470
488
expect ( authorizationUrl . toString ( ) ) . toMatch (
@@ -487,9 +505,9 @@ describe("OAuth Authorization", () => {
487
505
"https://auth.example.com" ,
488
506
{
489
507
clientInformation : validClientInfo ,
490
- redirectUrl : "http://localhost:3000/callback" ,
491
508
scope : "read write profile" ,
492
- }
509
+ } ,
510
+ mockProvider
493
511
) ;
494
512
495
513
expect ( authorizationUrl . searchParams . get ( "scope" ) ) . toBe ( "read write profile" ) ;
@@ -500,8 +518,8 @@ describe("OAuth Authorization", () => {
500
518
"https://auth.example.com" ,
501
519
{
502
520
clientInformation : validClientInfo ,
503
- redirectUrl : "http://localhost:3000/callback" ,
504
- }
521
+ } ,
522
+ mockProvider
505
523
) ;
506
524
507
525
expect ( authorizationUrl . searchParams . has ( "scope" ) ) . toBe ( false ) ;
@@ -512,9 +530,9 @@ describe("OAuth Authorization", () => {
512
530
"https://auth.example.com" ,
513
531
{
514
532
clientInformation : validClientInfo ,
515
- redirectUrl : "http://localhost:3000/callback" ,
516
533
state : "foobar" ,
517
- }
534
+ } ,
535
+ mockProvider
518
536
) ;
519
537
520
538
expect ( authorizationUrl . searchParams . get ( "state" ) ) . toBe ( "foobar" ) ;
@@ -525,8 +543,8 @@ describe("OAuth Authorization", () => {
525
543
"https://auth.example.com" ,
526
544
{
527
545
clientInformation : validClientInfo ,
528
- redirectUrl : "http://localhost:3000/callback" ,
529
- }
546
+ } ,
547
+ mockProvider
530
548
) ;
531
549
532
550
expect ( authorizationUrl . searchParams . has ( "state" ) ) . toBe ( false ) ;
@@ -538,8 +556,8 @@ describe("OAuth Authorization", () => {
538
556
{
539
557
metadata : validMetadata ,
540
558
clientInformation : validClientInfo ,
541
- redirectUrl : "http://localhost:3000/callback" ,
542
- }
559
+ } ,
560
+ mockProvider
543
561
) ;
544
562
545
563
expect ( authorizationUrl . toString ( ) ) . toMatch (
@@ -557,8 +575,7 @@ describe("OAuth Authorization", () => {
557
575
startAuthorization ( "https://auth.example.com" , {
558
576
metadata,
559
577
clientInformation : validClientInfo ,
560
- redirectUrl : "http://localhost:3000/callback" ,
561
- } )
578
+ } , mockProvider )
562
579
) . rejects . toThrow ( / d o e s n o t s u p p o r t r e s p o n s e t y p e / ) ;
563
580
} ) ;
564
581
@@ -573,29 +590,12 @@ describe("OAuth Authorization", () => {
573
590
startAuthorization ( "https://auth.example.com" , {
574
591
metadata,
575
592
clientInformation : validClientInfo ,
576
- redirectUrl : "http://localhost:3000/callback" ,
577
- } )
593
+ } , mockProvider )
578
594
) . rejects . toThrow ( / d o e s n o t s u p p o r t c o d e c h a l l e n g e m e t h o d / ) ;
579
595
} ) ;
580
596
} ) ;
581
597
582
598
describe ( "exchangeAuthorization" , ( ) => {
583
- const mockProvider : OAuthClientProvider = {
584
- get redirectUrl ( ) { return "http://localhost:3000/callback" ; } ,
585
- get clientMetadata ( ) {
586
- return {
587
- redirect_uris : [ "http://localhost:3000/callback" ] ,
588
- client_name : "Test Client" ,
589
- } ;
590
- } ,
591
- clientInformation : jest . fn ( ) ,
592
- tokens : jest . fn ( ) ,
593
- saveTokens : jest . fn ( ) ,
594
- redirectToAuthorization : jest . fn ( ) ,
595
- saveCodeVerifier : jest . fn ( ) ,
596
- codeVerifier : jest . fn ( ) ,
597
- } ;
598
-
599
599
const validTokens = {
600
600
access_token : "access123" ,
601
601
token_type : "Bearer" ,
@@ -620,10 +620,8 @@ describe("OAuth Authorization", () => {
620
620
const tokens = await exchangeAuthorization ( "https://auth.example.com" , {
621
621
clientInformation : validClientInfo ,
622
622
authorizationCode : "code123" ,
623
- codeVerifier : "verifier123" ,
624
- redirectUri : "http://localhost:3000/callback" ,
625
623
resource : new URL ( "https://api.example.com/mcp-server" ) ,
626
- } ) ;
624
+ } , mockProvider ) ;
627
625
628
626
expect ( tokens ) . toEqual ( validTokens ) ;
629
627
expect ( mockFetch ) . toHaveBeenCalledWith (
@@ -632,11 +630,12 @@ describe("OAuth Authorization", () => {
632
630
} ) ,
633
631
expect . objectContaining ( {
634
632
method : "POST" ,
633
+ headers : new Headers ( {
634
+ "Content-Type" : "application/x-www-form-urlencoded" ,
635
+ } ) ,
635
636
} )
636
637
) ;
637
638
638
- const headers = mockFetch . mock . calls [ 0 ] [ 1 ] . headers as Headers ;
639
- expect ( headers . get ( "Content-Type" ) ) . toBe ( "application/x-www-form-urlencoded" ) ;
640
639
const body = mockFetch . mock . calls [ 0 ] [ 1 ] . body as URLSearchParams ;
641
640
expect ( body . get ( "grant_type" ) ) . toBe ( "authorization_code" ) ;
642
641
expect ( body . get ( "code" ) ) . toBe ( "code123" ) ;
@@ -663,8 +662,6 @@ describe("OAuth Authorization", () => {
663
662
const tokens = await exchangeAuthorization ( "https://auth.example.com" , {
664
663
clientInformation : validClientInfo ,
665
664
authorizationCode : "code123" ,
666
- codeVerifier : "verifier123" ,
667
- redirectUri : "http://localhost:3000/callback" ,
668
665
} , mockProvider ) ;
669
666
670
667
expect ( tokens ) . toEqual ( validTokens ) ;
@@ -705,9 +702,7 @@ describe("OAuth Authorization", () => {
705
702
exchangeAuthorization ( "https://auth.example.com" , {
706
703
clientInformation : validClientInfo ,
707
704
authorizationCode : "code123" ,
708
- codeVerifier : "verifier123" ,
709
- redirectUri : "http://localhost:3000/callback" ,
710
- } )
705
+ } , mockProvider )
711
706
) . rejects . toThrow ( ) ;
712
707
} ) ;
713
708
@@ -721,30 +716,12 @@ describe("OAuth Authorization", () => {
721
716
exchangeAuthorization ( "https://auth.example.com" , {
722
717
clientInformation : validClientInfo ,
723
718
authorizationCode : "code123" ,
724
- codeVerifier : "verifier123" ,
725
- redirectUri : "http://localhost:3000/callback" ,
726
- } )
719
+ } , mockProvider )
727
720
) . rejects . toThrow ( "Token exchange failed" ) ;
728
721
} ) ;
729
722
} ) ;
730
723
731
724
describe ( "refreshAuthorization" , ( ) => {
732
- const mockProvider : OAuthClientProvider = {
733
- get redirectUrl ( ) { return "http://localhost:3000/callback" ; } ,
734
- get clientMetadata ( ) {
735
- return {
736
- redirect_uris : [ "http://localhost:3000/callback" ] ,
737
- client_name : "Test Client" ,
738
- } ;
739
- } ,
740
- clientInformation : jest . fn ( ) ,
741
- tokens : jest . fn ( ) ,
742
- saveTokens : jest . fn ( ) ,
743
- redirectToAuthorization : jest . fn ( ) ,
744
- saveCodeVerifier : jest . fn ( ) ,
745
- codeVerifier : jest . fn ( ) ,
746
- } ;
747
-
748
725
const validTokens = {
749
726
access_token : "newaccess123" ,
750
727
token_type : "Bearer" ,
@@ -773,7 +750,7 @@ describe("OAuth Authorization", () => {
773
750
clientInformation : validClientInfo ,
774
751
refreshToken : "refresh123" ,
775
752
resource : new URL ( "https://api.example.com/mcp-server" ) ,
776
- } ) ;
753
+ } , mockProvider ) ;
777
754
778
755
expect ( tokens ) . toEqual ( validTokensWithNewRefreshToken ) ;
779
756
expect ( mockFetch ) . toHaveBeenCalledWith (
@@ -785,8 +762,6 @@ describe("OAuth Authorization", () => {
785
762
} )
786
763
) ;
787
764
788
- const headers = mockFetch . mock . calls [ 0 ] [ 1 ] . headers as Headers ;
789
- expect ( headers . get ( "Content-Type" ) ) . toBe ( "application/x-www-form-urlencoded" ) ;
790
765
const body = mockFetch . mock . calls [ 0 ] [ 1 ] . body as URLSearchParams ;
791
766
expect ( body . get ( "grant_type" ) ) . toBe ( "refresh_token" ) ;
792
767
expect ( body . get ( "refresh_token" ) ) . toBe ( "refresh123" ) ;
@@ -846,7 +821,7 @@ describe("OAuth Authorization", () => {
846
821
const tokens = await refreshAuthorization ( "https://auth.example.com" , {
847
822
clientInformation : validClientInfo ,
848
823
refreshToken,
849
- } ) ;
824
+ } , mockProvider ) ;
850
825
851
826
expect ( tokens ) . toEqual ( { refresh_token : refreshToken , ...validTokens } ) ;
852
827
} ) ;
@@ -865,7 +840,7 @@ describe("OAuth Authorization", () => {
865
840
refreshAuthorization ( "https://auth.example.com" , {
866
841
clientInformation : validClientInfo ,
867
842
refreshToken : "refresh123" ,
868
- } )
843
+ } , mockProvider )
869
844
) . rejects . toThrow ( ) ;
870
845
} ) ;
871
846
@@ -879,7 +854,7 @@ describe("OAuth Authorization", () => {
879
854
refreshAuthorization ( "https://auth.example.com" , {
880
855
clientInformation : validClientInfo ,
881
856
refreshToken : "refresh123" ,
882
- } )
857
+ } , mockProvider )
883
858
) . rejects . toThrow ( "Token refresh failed" ) ;
884
859
} ) ;
885
860
} ) ;
@@ -1624,9 +1599,7 @@ describe("OAuth Authorization", () => {
1624
1599
metadata : metadataWithBasicOnly ,
1625
1600
clientInformation : validClientInfo ,
1626
1601
authorizationCode : "code123" ,
1627
- codeVerifier : "verifier123" ,
1628
- redirectUri : "http://localhost:3000/callback" ,
1629
- } ) ;
1602
+ } , mockProvider ) ;
1630
1603
1631
1604
expect ( tokens ) . toEqual ( validTokens ) ;
1632
1605
const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1652,9 +1625,7 @@ describe("OAuth Authorization", () => {
1652
1625
metadata : metadataWithPostOnly ,
1653
1626
clientInformation : validClientInfo ,
1654
1627
authorizationCode : "code123" ,
1655
- codeVerifier : "verifier123" ,
1656
- redirectUri : "http://localhost:3000/callback" ,
1657
- } ) ;
1628
+ } , mockProvider ) ;
1658
1629
1659
1630
expect ( tokens ) . toEqual ( validTokens ) ;
1660
1631
const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1684,9 +1655,7 @@ describe("OAuth Authorization", () => {
1684
1655
metadata : metadataWithNoneOnly ,
1685
1656
clientInformation : clientInfoWithoutSecret ,
1686
1657
authorizationCode : "code123" ,
1687
- codeVerifier : "verifier123" ,
1688
- redirectUri : "http://localhost:3000/callback" ,
1689
- } ) ;
1658
+ } , mockProvider ) ;
1690
1659
1691
1660
expect ( tokens ) . toEqual ( validTokens ) ;
1692
1661
const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1709,14 +1678,13 @@ describe("OAuth Authorization", () => {
1709
1678
const tokens = await exchangeAuthorization ( "https://auth.example.com" , {
1710
1679
clientInformation : validClientInfo ,
1711
1680
authorizationCode : "code123" ,
1712
- codeVerifier : "verifier123" ,
1713
- redirectUri : "http://localhost:3000/callback" ,
1714
- } ) ;
1681
+ } , mockProvider ) ;
1715
1682
1716
1683
expect ( tokens ) . toEqual ( validTokens ) ;
1717
1684
const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
1718
1685
1719
- // Check no Authorization header
1686
+ // Check headers
1687
+ expect ( request . headers . get ( "Content-Type" ) ) . toBe ( "application/x-www-form-urlencoded" ) ;
1720
1688
expect ( request . headers . get ( "Authorization" ) ) . toBeNull ( ) ;
1721
1689
1722
1690
const body = request . body as URLSearchParams ;
@@ -1764,7 +1732,7 @@ describe("OAuth Authorization", () => {
1764
1732
metadata : metadataWithBasicOnly ,
1765
1733
clientInformation : validClientInfo ,
1766
1734
refreshToken : "refresh123" ,
1767
- } ) ;
1735
+ } , mockProvider ) ;
1768
1736
1769
1737
expect ( tokens ) . toEqual ( validTokens ) ;
1770
1738
const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1791,7 +1759,7 @@ describe("OAuth Authorization", () => {
1791
1759
metadata : metadataWithPostOnly ,
1792
1760
clientInformation : validClientInfo ,
1793
1761
refreshToken : "refresh123" ,
1794
- } ) ;
1762
+ } , mockProvider ) ;
1795
1763
1796
1764
expect ( tokens ) . toEqual ( validTokens ) ;
1797
1765
const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
0 commit comments