1+ import { mock , Mock } from 'node:test' ;
12import { LATEST_PROTOCOL_VERSION } from '../types.js' ;
23import {
34 discoverOAuthMetadata ,
@@ -16,8 +17,25 @@ const mockFetch = jest.fn();
1617global . fetch = mockFetch ;
1718
1819describe ( "OAuth Authorization" , ( ) => {
20+ let mockProvider : OAuthClientProvider ;
21+
1922 beforeEach ( ( ) => {
2023 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+ } ;
2139 } ) ;
2240
2341 describe ( "extractResourceMetadataUrl" , ( ) => {
@@ -462,9 +480,9 @@ describe("OAuth Authorization", () => {
462480 {
463481 metadata : undefined ,
464482 clientInformation : validClientInfo ,
465- redirectUrl : "http://localhost:3000/callback" ,
466483 resource : new URL ( "https://api.example.com/mcp-server" ) ,
467- }
484+ } ,
485+ mockProvider
468486 ) ;
469487
470488 expect ( authorizationUrl . toString ( ) ) . toMatch (
@@ -487,9 +505,9 @@ describe("OAuth Authorization", () => {
487505 "https://auth.example.com" ,
488506 {
489507 clientInformation : validClientInfo ,
490- redirectUrl : "http://localhost:3000/callback" ,
491508 scope : "read write profile" ,
492- }
509+ } ,
510+ mockProvider
493511 ) ;
494512
495513 expect ( authorizationUrl . searchParams . get ( "scope" ) ) . toBe ( "read write profile" ) ;
@@ -500,8 +518,8 @@ describe("OAuth Authorization", () => {
500518 "https://auth.example.com" ,
501519 {
502520 clientInformation : validClientInfo ,
503- redirectUrl : "http://localhost:3000/callback" ,
504- }
521+ } ,
522+ mockProvider
505523 ) ;
506524
507525 expect ( authorizationUrl . searchParams . has ( "scope" ) ) . toBe ( false ) ;
@@ -512,9 +530,9 @@ describe("OAuth Authorization", () => {
512530 "https://auth.example.com" ,
513531 {
514532 clientInformation : validClientInfo ,
515- redirectUrl : "http://localhost:3000/callback" ,
516533 state : "foobar" ,
517- }
534+ } ,
535+ mockProvider
518536 ) ;
519537
520538 expect ( authorizationUrl . searchParams . get ( "state" ) ) . toBe ( "foobar" ) ;
@@ -525,8 +543,8 @@ describe("OAuth Authorization", () => {
525543 "https://auth.example.com" ,
526544 {
527545 clientInformation : validClientInfo ,
528- redirectUrl : "http://localhost:3000/callback" ,
529- }
546+ } ,
547+ mockProvider
530548 ) ;
531549
532550 expect ( authorizationUrl . searchParams . has ( "state" ) ) . toBe ( false ) ;
@@ -538,8 +556,8 @@ describe("OAuth Authorization", () => {
538556 {
539557 metadata : validMetadata ,
540558 clientInformation : validClientInfo ,
541- redirectUrl : "http://localhost:3000/callback" ,
542- }
559+ } ,
560+ mockProvider
543561 ) ;
544562
545563 expect ( authorizationUrl . toString ( ) ) . toMatch (
@@ -557,8 +575,7 @@ describe("OAuth Authorization", () => {
557575 startAuthorization ( "https://auth.example.com" , {
558576 metadata,
559577 clientInformation : validClientInfo ,
560- redirectUrl : "http://localhost:3000/callback" ,
561- } )
578+ } , mockProvider )
562579 ) . 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 / ) ;
563580 } ) ;
564581
@@ -573,29 +590,12 @@ describe("OAuth Authorization", () => {
573590 startAuthorization ( "https://auth.example.com" , {
574591 metadata,
575592 clientInformation : validClientInfo ,
576- redirectUrl : "http://localhost:3000/callback" ,
577- } )
593+ } , mockProvider )
578594 ) . 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 / ) ;
579595 } ) ;
580596 } ) ;
581597
582598 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-
599599 const validTokens = {
600600 access_token : "access123" ,
601601 token_type : "Bearer" ,
@@ -620,10 +620,8 @@ describe("OAuth Authorization", () => {
620620 const tokens = await exchangeAuthorization ( "https://auth.example.com" , {
621621 clientInformation : validClientInfo ,
622622 authorizationCode : "code123" ,
623- codeVerifier : "verifier123" ,
624- redirectUri : "http://localhost:3000/callback" ,
625623 resource : new URL ( "https://api.example.com/mcp-server" ) ,
626- } ) ;
624+ } , mockProvider ) ;
627625
628626 expect ( tokens ) . toEqual ( validTokens ) ;
629627 expect ( mockFetch ) . toHaveBeenCalledWith (
@@ -632,11 +630,12 @@ describe("OAuth Authorization", () => {
632630 } ) ,
633631 expect . objectContaining ( {
634632 method : "POST" ,
633+ headers : new Headers ( {
634+ "Content-Type" : "application/x-www-form-urlencoded" ,
635+ } ) ,
635636 } )
636637 ) ;
637638
638- const headers = mockFetch . mock . calls [ 0 ] [ 1 ] . headers as Headers ;
639- expect ( headers . get ( "Content-Type" ) ) . toBe ( "application/x-www-form-urlencoded" ) ;
640639 const body = mockFetch . mock . calls [ 0 ] [ 1 ] . body as URLSearchParams ;
641640 expect ( body . get ( "grant_type" ) ) . toBe ( "authorization_code" ) ;
642641 expect ( body . get ( "code" ) ) . toBe ( "code123" ) ;
@@ -663,8 +662,6 @@ describe("OAuth Authorization", () => {
663662 const tokens = await exchangeAuthorization ( "https://auth.example.com" , {
664663 clientInformation : validClientInfo ,
665664 authorizationCode : "code123" ,
666- codeVerifier : "verifier123" ,
667- redirectUri : "http://localhost:3000/callback" ,
668665 } , mockProvider ) ;
669666
670667 expect ( tokens ) . toEqual ( validTokens ) ;
@@ -705,9 +702,7 @@ describe("OAuth Authorization", () => {
705702 exchangeAuthorization ( "https://auth.example.com" , {
706703 clientInformation : validClientInfo ,
707704 authorizationCode : "code123" ,
708- codeVerifier : "verifier123" ,
709- redirectUri : "http://localhost:3000/callback" ,
710- } )
705+ } , mockProvider )
711706 ) . rejects . toThrow ( ) ;
712707 } ) ;
713708
@@ -721,30 +716,12 @@ describe("OAuth Authorization", () => {
721716 exchangeAuthorization ( "https://auth.example.com" , {
722717 clientInformation : validClientInfo ,
723718 authorizationCode : "code123" ,
724- codeVerifier : "verifier123" ,
725- redirectUri : "http://localhost:3000/callback" ,
726- } )
719+ } , mockProvider )
727720 ) . rejects . toThrow ( "Token exchange failed" ) ;
728721 } ) ;
729722 } ) ;
730723
731724 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-
748725 const validTokens = {
749726 access_token : "newaccess123" ,
750727 token_type : "Bearer" ,
@@ -773,7 +750,7 @@ describe("OAuth Authorization", () => {
773750 clientInformation : validClientInfo ,
774751 refreshToken : "refresh123" ,
775752 resource : new URL ( "https://api.example.com/mcp-server" ) ,
776- } ) ;
753+ } , mockProvider ) ;
777754
778755 expect ( tokens ) . toEqual ( validTokensWithNewRefreshToken ) ;
779756 expect ( mockFetch ) . toHaveBeenCalledWith (
@@ -785,8 +762,6 @@ describe("OAuth Authorization", () => {
785762 } )
786763 ) ;
787764
788- const headers = mockFetch . mock . calls [ 0 ] [ 1 ] . headers as Headers ;
789- expect ( headers . get ( "Content-Type" ) ) . toBe ( "application/x-www-form-urlencoded" ) ;
790765 const body = mockFetch . mock . calls [ 0 ] [ 1 ] . body as URLSearchParams ;
791766 expect ( body . get ( "grant_type" ) ) . toBe ( "refresh_token" ) ;
792767 expect ( body . get ( "refresh_token" ) ) . toBe ( "refresh123" ) ;
@@ -846,7 +821,7 @@ describe("OAuth Authorization", () => {
846821 const tokens = await refreshAuthorization ( "https://auth.example.com" , {
847822 clientInformation : validClientInfo ,
848823 refreshToken,
849- } ) ;
824+ } , mockProvider ) ;
850825
851826 expect ( tokens ) . toEqual ( { refresh_token : refreshToken , ...validTokens } ) ;
852827 } ) ;
@@ -865,7 +840,7 @@ describe("OAuth Authorization", () => {
865840 refreshAuthorization ( "https://auth.example.com" , {
866841 clientInformation : validClientInfo ,
867842 refreshToken : "refresh123" ,
868- } )
843+ } , mockProvider )
869844 ) . rejects . toThrow ( ) ;
870845 } ) ;
871846
@@ -879,7 +854,7 @@ describe("OAuth Authorization", () => {
879854 refreshAuthorization ( "https://auth.example.com" , {
880855 clientInformation : validClientInfo ,
881856 refreshToken : "refresh123" ,
882- } )
857+ } , mockProvider )
883858 ) . rejects . toThrow ( "Token refresh failed" ) ;
884859 } ) ;
885860 } ) ;
@@ -1624,9 +1599,7 @@ describe("OAuth Authorization", () => {
16241599 metadata : metadataWithBasicOnly ,
16251600 clientInformation : validClientInfo ,
16261601 authorizationCode : "code123" ,
1627- codeVerifier : "verifier123" ,
1628- redirectUri : "http://localhost:3000/callback" ,
1629- } ) ;
1602+ } , mockProvider ) ;
16301603
16311604 expect ( tokens ) . toEqual ( validTokens ) ;
16321605 const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1652,9 +1625,7 @@ describe("OAuth Authorization", () => {
16521625 metadata : metadataWithPostOnly ,
16531626 clientInformation : validClientInfo ,
16541627 authorizationCode : "code123" ,
1655- codeVerifier : "verifier123" ,
1656- redirectUri : "http://localhost:3000/callback" ,
1657- } ) ;
1628+ } , mockProvider ) ;
16581629
16591630 expect ( tokens ) . toEqual ( validTokens ) ;
16601631 const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1684,9 +1655,7 @@ describe("OAuth Authorization", () => {
16841655 metadata : metadataWithNoneOnly ,
16851656 clientInformation : clientInfoWithoutSecret ,
16861657 authorizationCode : "code123" ,
1687- codeVerifier : "verifier123" ,
1688- redirectUri : "http://localhost:3000/callback" ,
1689- } ) ;
1658+ } , mockProvider ) ;
16901659
16911660 expect ( tokens ) . toEqual ( validTokens ) ;
16921661 const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1709,14 +1678,13 @@ describe("OAuth Authorization", () => {
17091678 const tokens = await exchangeAuthorization ( "https://auth.example.com" , {
17101679 clientInformation : validClientInfo ,
17111680 authorizationCode : "code123" ,
1712- codeVerifier : "verifier123" ,
1713- redirectUri : "http://localhost:3000/callback" ,
1714- } ) ;
1681+ } , mockProvider ) ;
17151682
17161683 expect ( tokens ) . toEqual ( validTokens ) ;
17171684 const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
17181685
1719- // Check no Authorization header
1686+ // Check headers
1687+ expect ( request . headers . get ( "Content-Type" ) ) . toBe ( "application/x-www-form-urlencoded" ) ;
17201688 expect ( request . headers . get ( "Authorization" ) ) . toBeNull ( ) ;
17211689
17221690 const body = request . body as URLSearchParams ;
@@ -1764,7 +1732,7 @@ describe("OAuth Authorization", () => {
17641732 metadata : metadataWithBasicOnly ,
17651733 clientInformation : validClientInfo ,
17661734 refreshToken : "refresh123" ,
1767- } ) ;
1735+ } , mockProvider ) ;
17681736
17691737 expect ( tokens ) . toEqual ( validTokens ) ;
17701738 const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
@@ -1791,7 +1759,7 @@ describe("OAuth Authorization", () => {
17911759 metadata : metadataWithPostOnly ,
17921760 clientInformation : validClientInfo ,
17931761 refreshToken : "refresh123" ,
1794- } ) ;
1762+ } , mockProvider ) ;
17951763
17961764 expect ( tokens ) . toEqual ( validTokens ) ;
17971765 const request = mockFetch . mock . calls [ 0 ] [ 1 ] ;
0 commit comments