@@ -15,9 +15,18 @@ import {
1515} from './auth.js' ;
1616import { ServerError } from '../server/auth/errors.js' ;
1717import { AuthorizationServerMetadata } from '../shared/auth.js' ;
18+ import { vi , type Mock } from 'vitest' ;
19+
20+ // Mock pkce-challenge
21+ vi . mock ( 'pkce-challenge' , ( ) => ( {
22+ default : ( ) => ( {
23+ code_verifier : 'test_verifier' ,
24+ code_challenge : 'test_challenge'
25+ } )
26+ } ) ) ;
1827
1928// Mock fetch globally
20- const mockFetch = jest . fn ( ) ;
29+ const mockFetch = vi . fn ( ) ;
2130global . fetch = mockFetch ;
2231
2332describe ( 'OAuth Authorization' , ( ) => {
@@ -30,7 +39,7 @@ describe('OAuth Authorization', () => {
3039 const resourceUrl = 'https://resource.example.com/.well-known/oauth-protected-resource' ;
3140 const mockResponse = {
3241 headers : {
33- get : jest . fn ( name => ( name === 'WWW-Authenticate' ? `Bearer realm="mcp", resource_metadata="${ resourceUrl } "` : null ) )
42+ get : vi . fn ( name => ( name === 'WWW-Authenticate' ? `Bearer realm="mcp", resource_metadata="${ resourceUrl } "` : null ) )
3443 }
3544 } as unknown as Response ;
3645
@@ -41,7 +50,7 @@ describe('OAuth Authorization', () => {
4150 const scope = 'read' ;
4251 const mockResponse = {
4352 headers : {
44- get : jest . fn ( name => ( name === 'WWW-Authenticate' ? `Bearer realm="mcp", scope="${ scope } "` : null ) )
53+ get : vi . fn ( name => ( name === 'WWW-Authenticate' ? `Bearer realm="mcp", scope="${ scope } "` : null ) )
4554 }
4655 } as unknown as Response ;
4756
@@ -53,7 +62,7 @@ describe('OAuth Authorization', () => {
5362 const scope = 'read' ;
5463 const mockResponse = {
5564 headers : {
56- get : jest . fn ( name =>
65+ get : vi . fn ( name =>
5766 name === 'WWW-Authenticate' ? `Basic realm="mcp", resource_metadata="${ resourceUrl } ", scope="${ scope } "` : null
5867 )
5968 }
@@ -65,7 +74,7 @@ describe('OAuth Authorization', () => {
6574 it ( 'returns empty object if resource_metadata and scope not present' , async ( ) => {
6675 const mockResponse = {
6776 headers : {
68- get : jest . fn ( name => ( name === 'WWW-Authenticate' ? `Bearer realm="mcp"` : null ) )
77+ get : vi . fn ( name => ( name === 'WWW-Authenticate' ? `Bearer realm="mcp"` : null ) )
6978 }
7079 } as unknown as Response ;
7180
@@ -77,7 +86,7 @@ describe('OAuth Authorization', () => {
7786 const scope = 'read' ;
7887 const mockResponse = {
7988 headers : {
80- get : jest . fn ( name =>
89+ get : vi . fn ( name =>
8190 name === 'WWW-Authenticate' ? `Bearer realm="mcp", resource_metadata="${ resourceUrl } ", scope="${ scope } "` : null
8291 )
8392 }
@@ -393,7 +402,7 @@ describe('OAuth Authorization', () => {
393402 authorization_servers : [ 'https://auth.example.com' ]
394403 } ;
395404
396- const customFetch = jest . fn ( ) . mockResolvedValue ( {
405+ const customFetch = vi . fn ( ) . mockResolvedValue ( {
397406 ok : true ,
398407 status : 200 ,
399408 json : async ( ) => validMetadata
@@ -689,7 +698,7 @@ describe('OAuth Authorization', () => {
689698 code_challenge_methods_supported : [ 'S256' ]
690699 } ;
691700
692- const customFetch = jest . fn ( ) . mockResolvedValue ( {
701+ const customFetch = vi . fn ( ) . mockResolvedValue ( {
693702 ok : true ,
694703 status : 200 ,
695704 json : async ( ) => validMetadata
@@ -851,7 +860,7 @@ describe('OAuth Authorization', () => {
851860 } ) ;
852861
853862 it ( 'supports custom fetch function' , async ( ) => {
854- const customFetch = jest . fn ( ) . mockResolvedValue ( {
863+ const customFetch = vi . fn ( ) . mockResolvedValue ( {
855864 ok : true ,
856865 status : 200 ,
857866 json : async ( ) => validOAuthMetadata
@@ -1110,17 +1119,9 @@ describe('OAuth Authorization', () => {
11101119 } ) ;
11111120
11121121 expect ( tokens ) . toEqual ( validTokens ) ;
1113- expect ( mockFetch ) . toHaveBeenCalledWith (
1114- expect . objectContaining ( {
1115- href : 'https://auth.example.com/token'
1116- } ) ,
1117- expect . objectContaining ( {
1118- method : 'POST' ,
1119- headers : new Headers ( {
1120- 'Content-Type' : 'application/x-www-form-urlencoded'
1121- } )
1122- } )
1123- ) ;
1122+ expect ( mockFetch ) . toHaveBeenCalledTimes ( 1 ) ;
1123+ expect ( mockFetch . mock . calls [ 0 ] [ 0 ] . toString ( ) ) . toBe ( 'https://auth.example.com/token' ) ;
1124+ expect ( mockFetch . mock . calls [ 0 ] [ 1 ] . method ) . toBe ( 'POST' ) ;
11241125
11251126 const body = mockFetch . mock . calls [ 0 ] [ 1 ] . body as URLSearchParams ;
11261127 expect ( body . get ( 'grant_type' ) ) . toBe ( 'authorization_code' ) ;
@@ -1217,7 +1218,7 @@ describe('OAuth Authorization', () => {
12171218 } ) ;
12181219
12191220 it ( 'supports overriding the fetch function used for requests' , async ( ) => {
1220- const customFetch = jest . fn ( ) . mockResolvedValue ( {
1221+ const customFetch = vi . fn ( ) . mockResolvedValue ( {
12211222 ok : true ,
12221223 status : 200 ,
12231224 json : async ( ) => validTokens
@@ -1506,16 +1507,16 @@ describe('OAuth Authorization', () => {
15061507 client_name : 'Test Client'
15071508 } ;
15081509 } ,
1509- clientInformation : jest . fn ( ) ,
1510- tokens : jest . fn ( ) ,
1511- saveTokens : jest . fn ( ) ,
1512- redirectToAuthorization : jest . fn ( ) ,
1513- saveCodeVerifier : jest . fn ( ) ,
1514- codeVerifier : jest . fn ( )
1510+ clientInformation : vi . fn ( ) ,
1511+ tokens : vi . fn ( ) ,
1512+ saveTokens : vi . fn ( ) ,
1513+ redirectToAuthorization : vi . fn ( ) ,
1514+ saveCodeVerifier : vi . fn ( ) ,
1515+ codeVerifier : vi . fn ( )
15151516 } ;
15161517
15171518 beforeEach ( ( ) => {
1518- jest . clearAllMocks ( ) ;
1519+ vi . clearAllMocks ( ) ;
15191520 } ) ;
15201521
15211522 it ( 'falls back to /.well-known/oauth-authorization-server when no protected-resource-metadata' , async ( ) => {
@@ -1567,9 +1568,9 @@ describe('OAuth Authorization', () => {
15671568 } ) ;
15681569
15691570 // Mock provider methods
1570- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( undefined ) ;
1571- ( mockProvider . tokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
1572- mockProvider . saveClientInformation = jest . fn ( ) ;
1571+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( undefined ) ;
1572+ ( mockProvider . tokens as Mock ) . mockResolvedValue ( undefined ) ;
1573+ mockProvider . saveClientInformation = vi . fn ( ) ;
15731574
15741575 // Call the auth function
15751576 const result = await auth ( mockProvider , {
@@ -1619,13 +1620,13 @@ describe('OAuth Authorization', () => {
16191620 } ) ;
16201621
16211622 // Mock provider methods for authorization flow
1622- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
1623+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
16231624 client_id : 'test-client' ,
16241625 client_secret : 'test-secret'
16251626 } ) ;
1626- ( mockProvider . tokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
1627- ( mockProvider . saveCodeVerifier as jest . Mock ) . mockResolvedValue ( undefined ) ;
1628- ( mockProvider . redirectToAuthorization as jest . Mock ) . mockResolvedValue ( undefined ) ;
1627+ ( mockProvider . tokens as Mock ) . mockResolvedValue ( undefined ) ;
1628+ ( mockProvider . saveCodeVerifier as Mock ) . mockResolvedValue ( undefined ) ;
1629+ ( mockProvider . redirectToAuthorization as Mock ) . mockResolvedValue ( undefined ) ;
16291630
16301631 // Call auth without authorization code (should trigger redirect)
16311632 const result = await auth ( mockProvider , {
@@ -1641,7 +1642,7 @@ describe('OAuth Authorization', () => {
16411642 } )
16421643 ) ;
16431644
1644- const redirectCall = ( mockProvider . redirectToAuthorization as jest . Mock ) . mock . calls [ 0 ] ;
1645+ const redirectCall = ( mockProvider . redirectToAuthorization as Mock ) . mock . calls [ 0 ] ;
16451646 const authUrl : URL = redirectCall [ 0 ] ;
16461647 expect ( authUrl . searchParams . get ( 'resource' ) ) . toBe ( 'https://api.example.com/mcp-server' ) ;
16471648 } ) ;
@@ -1689,12 +1690,12 @@ describe('OAuth Authorization', () => {
16891690 } ) ;
16901691
16911692 // Mock provider methods for token exchange
1692- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
1693+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
16931694 client_id : 'test-client' ,
16941695 client_secret : 'test-secret'
16951696 } ) ;
1696- ( mockProvider . codeVerifier as jest . Mock ) . mockResolvedValue ( 'test-verifier' ) ;
1697- ( mockProvider . saveTokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
1697+ ( mockProvider . codeVerifier as Mock ) . mockResolvedValue ( 'test-verifier' ) ;
1698+ ( mockProvider . saveTokens as Mock ) . mockResolvedValue ( undefined ) ;
16981699
16991700 // Call auth with authorization code
17001701 const result = await auth ( mockProvider , {
@@ -1755,15 +1756,15 @@ describe('OAuth Authorization', () => {
17551756 } ) ;
17561757
17571758 // Mock provider methods for token refresh
1758- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
1759+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
17591760 client_id : 'test-client' ,
17601761 client_secret : 'test-secret'
17611762 } ) ;
1762- ( mockProvider . tokens as jest . Mock ) . mockResolvedValue ( {
1763+ ( mockProvider . tokens as Mock ) . mockResolvedValue ( {
17631764 access_token : 'old-access' ,
17641765 refresh_token : 'refresh123'
17651766 } ) ;
1766- ( mockProvider . saveTokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
1767+ ( mockProvider . saveTokens as Mock ) . mockResolvedValue ( undefined ) ;
17671768
17681769 // Call auth with existing tokens (should trigger refresh)
17691770 const result = await auth ( mockProvider , {
@@ -1783,7 +1784,7 @@ describe('OAuth Authorization', () => {
17831784 } ) ;
17841785
17851786 it ( 'skips default PRM resource validation when custom validateResourceURL is provided' , async ( ) => {
1786- const mockValidateResourceURL = jest . fn ( ) . mockResolvedValue ( undefined ) ;
1787+ const mockValidateResourceURL = vi . fn ( ) . mockResolvedValue ( undefined ) ;
17871788 const providerWithCustomValidation = {
17881789 ...mockProvider ,
17891790 validateResourceURL : mockValidateResourceURL
@@ -1821,13 +1822,13 @@ describe('OAuth Authorization', () => {
18211822 } ) ;
18221823
18231824 // Mock provider methods
1824- ( providerWithCustomValidation . clientInformation as jest . Mock ) . mockResolvedValue ( {
1825+ ( providerWithCustomValidation . clientInformation as Mock ) . mockResolvedValue ( {
18251826 client_id : 'test-client' ,
18261827 client_secret : 'test-secret'
18271828 } ) ;
1828- ( providerWithCustomValidation . tokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
1829- ( providerWithCustomValidation . saveCodeVerifier as jest . Mock ) . mockResolvedValue ( undefined ) ;
1830- ( providerWithCustomValidation . redirectToAuthorization as jest . Mock ) . mockResolvedValue ( undefined ) ;
1829+ ( providerWithCustomValidation . tokens as Mock ) . mockResolvedValue ( undefined ) ;
1830+ ( providerWithCustomValidation . saveCodeVerifier as Mock ) . mockResolvedValue ( undefined ) ;
1831+ ( providerWithCustomValidation . redirectToAuthorization as Mock ) . mockResolvedValue ( undefined ) ;
18311832
18321833 // Call auth - should succeed despite resource mismatch because custom validation overrides default
18331834 const result = await auth ( providerWithCustomValidation , {
@@ -1876,13 +1877,13 @@ describe('OAuth Authorization', () => {
18761877 } ) ;
18771878
18781879 // Mock provider methods
1879- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
1880+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
18801881 client_id : 'test-client' ,
18811882 client_secret : 'test-secret'
18821883 } ) ;
1883- ( mockProvider . tokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
1884- ( mockProvider . saveCodeVerifier as jest . Mock ) . mockResolvedValue ( undefined ) ;
1885- ( mockProvider . redirectToAuthorization as jest . Mock ) . mockResolvedValue ( undefined ) ;
1884+ ( mockProvider . tokens as Mock ) . mockResolvedValue ( undefined ) ;
1885+ ( mockProvider . saveCodeVerifier as Mock ) . mockResolvedValue ( undefined ) ;
1886+ ( mockProvider . redirectToAuthorization as Mock ) . mockResolvedValue ( undefined ) ;
18861887
18871888 // Call auth with a URL that has the resource as prefix
18881889 const result = await auth ( mockProvider , {
@@ -1898,7 +1899,7 @@ describe('OAuth Authorization', () => {
18981899 } )
18991900 ) ;
19001901
1901- const redirectCall = ( mockProvider . redirectToAuthorization as jest . Mock ) . mock . calls [ 0 ] ;
1902+ const redirectCall = ( mockProvider . redirectToAuthorization as Mock ) . mock . calls [ 0 ] ;
19021903 const authUrl : URL = redirectCall [ 0 ] ;
19031904 // Should use the PRM's resource value, not the full requested URL
19041905 expect ( authUrl . searchParams . get ( 'resource' ) ) . toBe ( 'https://api.example.com/' ) ;
@@ -1934,13 +1935,13 @@ describe('OAuth Authorization', () => {
19341935 } ) ;
19351936
19361937 // Mock provider methods
1937- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
1938+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
19381939 client_id : 'test-client' ,
19391940 client_secret : 'test-secret'
19401941 } ) ;
1941- ( mockProvider . tokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
1942- ( mockProvider . saveCodeVerifier as jest . Mock ) . mockResolvedValue ( undefined ) ;
1943- ( mockProvider . redirectToAuthorization as jest . Mock ) . mockResolvedValue ( undefined ) ;
1942+ ( mockProvider . tokens as Mock ) . mockResolvedValue ( undefined ) ;
1943+ ( mockProvider . saveCodeVerifier as Mock ) . mockResolvedValue ( undefined ) ;
1944+ ( mockProvider . redirectToAuthorization as Mock ) . mockResolvedValue ( undefined ) ;
19441945
19451946 // Call auth - should not include resource parameter
19461947 const result = await auth ( mockProvider , {
@@ -1956,7 +1957,7 @@ describe('OAuth Authorization', () => {
19561957 } )
19571958 ) ;
19581959
1959- const redirectCall = ( mockProvider . redirectToAuthorization as jest . Mock ) . mock . calls [ 0 ] ;
1960+ const redirectCall = ( mockProvider . redirectToAuthorization as Mock ) . mock . calls [ 0 ] ;
19601961 const authUrl : URL = redirectCall [ 0 ] ;
19611962 // Resource parameter should not be present when PRM is not available
19621963 expect ( authUrl . searchParams . has ( 'resource' ) ) . toBe ( false ) ;
@@ -2001,12 +2002,12 @@ describe('OAuth Authorization', () => {
20012002 } ) ;
20022003
20032004 // Mock provider methods for token exchange
2004- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
2005+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
20052006 client_id : 'test-client' ,
20062007 client_secret : 'test-secret'
20072008 } ) ;
2008- ( mockProvider . codeVerifier as jest . Mock ) . mockResolvedValue ( 'test-verifier' ) ;
2009- ( mockProvider . saveTokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
2009+ ( mockProvider . codeVerifier as Mock ) . mockResolvedValue ( 'test-verifier' ) ;
2010+ ( mockProvider . saveTokens as Mock ) . mockResolvedValue ( undefined ) ;
20102011
20112012 // Call auth with authorization code
20122013 const result = await auth ( mockProvider , {
@@ -2064,15 +2065,15 @@ describe('OAuth Authorization', () => {
20642065 } ) ;
20652066
20662067 // Mock provider methods for token refresh
2067- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
2068+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
20682069 client_id : 'test-client' ,
20692070 client_secret : 'test-secret'
20702071 } ) ;
2071- ( mockProvider . tokens as jest . Mock ) . mockResolvedValue ( {
2072+ ( mockProvider . tokens as Mock ) . mockResolvedValue ( {
20722073 access_token : 'old-access' ,
20732074 refresh_token : 'refresh123'
20742075 } ) ;
2075- ( mockProvider . saveTokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
2076+ ( mockProvider . saveTokens as Mock ) . mockResolvedValue ( undefined ) ;
20762077
20772078 // Call auth with existing tokens (should trigger refresh)
20782079 const result = await auth ( mockProvider , {
@@ -2125,13 +2126,13 @@ describe('OAuth Authorization', () => {
21252126 } ) ;
21262127
21272128 // Mock provider methods
2128- ( mockProvider . clientInformation as jest . Mock ) . mockResolvedValue ( {
2129+ ( mockProvider . clientInformation as Mock ) . mockResolvedValue ( {
21292130 client_id : 'test-client' ,
21302131 client_secret : 'test-secret'
21312132 } ) ;
2132- ( mockProvider . tokens as jest . Mock ) . mockResolvedValue ( undefined ) ;
2133- ( mockProvider . saveCodeVerifier as jest . Mock ) . mockResolvedValue ( undefined ) ;
2134- ( mockProvider . redirectToAuthorization as jest . Mock ) . mockResolvedValue ( undefined ) ;
2133+ ( mockProvider . tokens as Mock ) . mockResolvedValue ( undefined ) ;
2134+ ( mockProvider . saveCodeVerifier as Mock ) . mockResolvedValue ( undefined ) ;
2135+ ( mockProvider . redirectToAuthorization as Mock ) . mockResolvedValue ( undefined ) ;
21352136
21362137 // Call auth with serverUrl that has a path
21372138 const result = await auth ( mockProvider , {
@@ -2151,7 +2152,7 @@ describe('OAuth Authorization', () => {
21512152 } ) ;
21522153
21532154 it ( 'supports overriding the fetch function used for requests' , async ( ) => {
2154- const customFetch = jest . fn ( ) ;
2155+ const customFetch = vi . fn ( ) ;
21552156
21562157 // Mock PRM discovery
21572158 customFetch . mockResolvedValueOnce ( {
@@ -2187,15 +2188,15 @@ describe('OAuth Authorization', () => {
21872188 redirect_uris : [ 'http://localhost:3000/callback' ]
21882189 } ;
21892190 } ,
2190- clientInformation : jest . fn ( ) . mockResolvedValue ( {
2191+ clientInformation : vi . fn ( ) . mockResolvedValue ( {
21912192 client_id : 'client123' ,
21922193 client_secret : 'secret123'
21932194 } ) ,
2194- tokens : jest . fn ( ) . mockResolvedValue ( undefined ) ,
2195- saveTokens : jest . fn ( ) ,
2196- redirectToAuthorization : jest . fn ( ) ,
2197- saveCodeVerifier : jest . fn ( ) ,
2198- codeVerifier : jest . fn ( ) . mockResolvedValue ( 'verifier123' )
2195+ tokens : vi . fn ( ) . mockResolvedValue ( undefined ) ,
2196+ saveTokens : vi . fn ( ) ,
2197+ redirectToAuthorization : vi . fn ( ) ,
2198+ saveCodeVerifier : vi . fn ( ) ,
2199+ codeVerifier : vi . fn ( ) . mockResolvedValue ( 'verifier123' )
21992200 } ;
22002201
22012202 const result = await auth ( mockProvider , {
0 commit comments