@@ -1045,5 +1045,156 @@ describe("OAuth Authorization", () => {
1045
1045
"https://different-resource.example.com/mcp-server"
1046
1046
) ;
1047
1047
} ) ;
1048
+
1049
+ describe ( "delegateAuthorization" , ( ) => {
1050
+ const validMetadata = {
1051
+ issuer : "https://auth.example.com" ,
1052
+ authorization_endpoint : "https://auth.example.com/authorize" ,
1053
+ token_endpoint : "https://auth.example.com/token" ,
1054
+ registration_endpoint : "https://auth.example.com/register" ,
1055
+ response_types_supported : [ "code" ] ,
1056
+ code_challenge_methods_supported : [ "S256" ] ,
1057
+ } ;
1058
+
1059
+ const validClientInfo = {
1060
+ client_id : "client123" ,
1061
+ client_secret : "secret123" ,
1062
+ redirect_uris : [ "http://localhost:3000/callback" ] ,
1063
+ client_name : "Test Client" ,
1064
+ } ;
1065
+
1066
+ const validTokens = {
1067
+ access_token : "access123" ,
1068
+ token_type : "Bearer" ,
1069
+ expires_in : 3600 ,
1070
+ refresh_token : "refresh123" ,
1071
+ } ;
1072
+
1073
+ // Setup shared mock function for all tests
1074
+ beforeEach ( ( ) => {
1075
+ // Reset mockFetch implementation
1076
+ mockFetch . mockReset ( ) ;
1077
+
1078
+ // Set up the mockFetch to respond to all necessary API calls
1079
+ mockFetch . mockImplementation ( ( url ) => {
1080
+ const urlString = url . toString ( ) ;
1081
+
1082
+ if ( urlString . includes ( "/.well-known/oauth-protected-resource" ) ) {
1083
+ return Promise . resolve ( {
1084
+ ok : false ,
1085
+ status : 404
1086
+ } ) ;
1087
+ } else if ( urlString . includes ( "/.well-known/oauth-authorization-server" ) ) {
1088
+ return Promise . resolve ( {
1089
+ ok : true ,
1090
+ status : 200 ,
1091
+ json : async ( ) => validMetadata
1092
+ } ) ;
1093
+ } else if ( urlString . includes ( "/token" ) ) {
1094
+ return Promise . resolve ( {
1095
+ ok : true ,
1096
+ status : 200 ,
1097
+ json : async ( ) => validTokens
1098
+ } ) ;
1099
+ }
1100
+
1101
+ return Promise . reject ( new Error ( `Unexpected fetch call: ${ urlString } ` ) ) ;
1102
+ } ) ;
1103
+ } ) ;
1104
+
1105
+ it ( "should use delegateAuthorization when implemented and return AUTHORIZED" , async ( ) => {
1106
+ const mockProvider : OAuthClientProvider = {
1107
+ redirectUrl : "http://localhost:3000/callback" ,
1108
+ clientMetadata : {
1109
+ redirect_uris : [ "http://localhost:3000/callback" ] ,
1110
+ client_name : "Test Client"
1111
+ } ,
1112
+ clientInformation : ( ) => validClientInfo ,
1113
+ tokens : ( ) => validTokens ,
1114
+ saveTokens : jest . fn ( ) ,
1115
+ redirectToAuthorization : jest . fn ( ) ,
1116
+ saveCodeVerifier : jest . fn ( ) ,
1117
+ codeVerifier : ( ) => "test_verifier" ,
1118
+ delegateAuthorization : jest . fn ( ) . mockResolvedValue ( "AUTHORIZED" )
1119
+ } ;
1120
+
1121
+ const result = await auth ( mockProvider , { serverUrl : "https://auth.example.com" } ) ;
1122
+
1123
+ expect ( result ) . toBe ( "AUTHORIZED" ) ;
1124
+ expect ( mockProvider . delegateAuthorization ) . toHaveBeenCalledWith (
1125
+ "https://auth.example.com" ,
1126
+ expect . objectContaining ( validMetadata )
1127
+ ) ;
1128
+ expect ( mockProvider . redirectToAuthorization ) . not . toHaveBeenCalled ( ) ;
1129
+ } ) ;
1130
+
1131
+ it ( "should fall back to standard flow when delegateAuthorization returns undefined" , async ( ) => {
1132
+ const mockProvider : OAuthClientProvider = {
1133
+ redirectUrl : "http://localhost:3000/callback" ,
1134
+ clientMetadata : {
1135
+ redirect_uris : [ "http://localhost:3000/callback" ] ,
1136
+ client_name : "Test Client"
1137
+ } ,
1138
+ clientInformation : ( ) => validClientInfo ,
1139
+ tokens : ( ) => validTokens ,
1140
+ saveTokens : jest . fn ( ) ,
1141
+ redirectToAuthorization : jest . fn ( ) ,
1142
+ saveCodeVerifier : jest . fn ( ) ,
1143
+ codeVerifier : ( ) => "test_verifier" ,
1144
+ delegateAuthorization : jest . fn ( ) . mockResolvedValue ( undefined )
1145
+ } ;
1146
+
1147
+ const result = await auth ( mockProvider , { serverUrl : "https://auth.example.com" } ) ;
1148
+
1149
+ expect ( result ) . toBe ( "AUTHORIZED" ) ;
1150
+ expect ( mockProvider . delegateAuthorization ) . toHaveBeenCalled ( ) ;
1151
+ expect ( mockProvider . saveTokens ) . toHaveBeenCalled ( ) ;
1152
+ } ) ;
1153
+
1154
+ it ( "should not call delegateAuthorization when processing authorizationCode" , async ( ) => {
1155
+ const mockProvider : OAuthClientProvider = {
1156
+ redirectUrl : "http://localhost:3000/callback" ,
1157
+ clientMetadata : {
1158
+ redirect_uris : [ "http://localhost:3000/callback" ] ,
1159
+ client_name : "Test Client"
1160
+ } ,
1161
+ clientInformation : ( ) => validClientInfo ,
1162
+ tokens : jest . fn ( ) ,
1163
+ saveTokens : jest . fn ( ) ,
1164
+ redirectToAuthorization : jest . fn ( ) ,
1165
+ saveCodeVerifier : jest . fn ( ) ,
1166
+ codeVerifier : ( ) => "test_verifier" ,
1167
+ delegateAuthorization : jest . fn ( )
1168
+ } ;
1169
+
1170
+ await auth ( mockProvider , {
1171
+ serverUrl : "https://auth.example.com" ,
1172
+ authorizationCode : "code123"
1173
+ } ) ;
1174
+
1175
+ expect ( mockProvider . delegateAuthorization ) . not . toHaveBeenCalled ( ) ;
1176
+ expect ( mockProvider . saveTokens ) . toHaveBeenCalled ( ) ;
1177
+ } ) ;
1178
+
1179
+ it ( "should propagate errors from delegateAuthorization" , async ( ) => {
1180
+ const mockProvider : OAuthClientProvider = {
1181
+ redirectUrl : "http://localhost:3000/callback" ,
1182
+ clientMetadata : {
1183
+ redirect_uris : [ "http://localhost:3000/callback" ] ,
1184
+ client_name : "Test Client"
1185
+ } ,
1186
+ clientInformation : ( ) => validClientInfo ,
1187
+ tokens : jest . fn ( ) ,
1188
+ saveTokens : jest . fn ( ) ,
1189
+ redirectToAuthorization : jest . fn ( ) ,
1190
+ saveCodeVerifier : jest . fn ( ) ,
1191
+ codeVerifier : ( ) => "test_verifier" ,
1192
+ delegateAuthorization : jest . fn ( ) . mockRejectedValue ( new Error ( "Delegation failed" ) )
1193
+ } ;
1194
+
1195
+ await expect ( auth ( mockProvider , { serverUrl : "https://auth.example.com" } ) )
1196
+ . rejects . toThrow ( "Delegation failed" ) ;
1197
+ } ) ;
1198
+ } ) ;
1048
1199
} ) ;
1049
1200
} ) ;
0 commit comments