@@ -290,4 +290,92 @@ describe("OAuth2 MCP Client", () => {
290290 expect ( response . status ) . toBeGreaterThanOrEqual ( 400 ) ;
291291 } ) ;
292292 } ) ;
293+
294+ describe ( "OAuth Redirect Behavior" , ( ) => {
295+ async function setupOAuthTest ( config : {
296+ successRedirect ?: string ;
297+ errorRedirect ?: string ;
298+ origin ?: string ;
299+ } ) {
300+ const agentId = env . TestOAuthAgent . newUniqueId ( ) ;
301+ const agentStub = env . TestOAuthAgent . get ( agentId ) ;
302+ await agentStub . setName ( "default" ) ;
303+ await agentStub . onStart ( ) ;
304+ await agentStub . configureOAuthForTest ( config ) ;
305+
306+ const serverId = nanoid ( 8 ) ;
307+ const origin = config . origin || "http://example.com" ;
308+ const callbackBaseUrl = `${ origin } /agents/oauth/${ agentId . toString ( ) } /callback` ;
309+
310+ agentStub . sql `
311+ INSERT INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
312+ VALUES (${ serverId } , ${ "test" } , ${ "http://example.com/mcp" } , ${ "client" } , ${ "http://example.com/auth" } , ${ callbackBaseUrl } , ${ null } )
313+ ` ;
314+
315+ await agentStub . setupMockMcpConnection (
316+ serverId ,
317+ "test" ,
318+ "http://example.com/mcp" ,
319+ callbackBaseUrl
320+ ) ;
321+ await agentStub . setupMockOAuthState ( serverId , "test-code" , "test-state" ) ;
322+
323+ return { agentStub, serverId, callbackBaseUrl } ;
324+ }
325+
326+ it ( "should return 302 redirect with Location header on successful OAuth callback" , async ( ) => {
327+ const { agentStub, serverId, callbackBaseUrl } = await setupOAuthTest ( {
328+ successRedirect : "/dashboard"
329+ } ) ;
330+
331+ const response = await agentStub . fetch (
332+ new Request (
333+ `${ callbackBaseUrl } /${ serverId } ?code=test-code&state=test-state` ,
334+ { method : "GET" , redirect : "manual" }
335+ )
336+ ) ;
337+
338+ expect ( response . status ) . toBe ( 302 ) ;
339+ expect ( response . headers . get ( "Location" ) ) . toBe (
340+ "http://example.com/dashboard"
341+ ) ;
342+ } ) ;
343+
344+ it ( "should handle relative URLs in successRedirect" , async ( ) => {
345+ const { agentStub, serverId, callbackBaseUrl } = await setupOAuthTest ( {
346+ successRedirect : "/success" ,
347+ origin : "http://test.local"
348+ } ) ;
349+
350+ const response = await agentStub . fetch (
351+ new Request (
352+ `${ callbackBaseUrl } /${ serverId } ?code=test-code&state=test-state` ,
353+ { method : "GET" , redirect : "manual" }
354+ )
355+ ) ;
356+
357+ expect ( response . status ) . toBe ( 302 ) ;
358+ expect ( response . headers . get ( "Location" ) ) . toBe (
359+ "http://test.local/success"
360+ ) ;
361+ } ) ;
362+
363+ it ( "should redirect to errorRedirect with error parameter on OAuth failure" , async ( ) => {
364+ const { agentStub, serverId, callbackBaseUrl } = await setupOAuthTest ( {
365+ errorRedirect : "/error"
366+ } ) ;
367+
368+ const response = await agentStub . fetch (
369+ new Request (
370+ `${ callbackBaseUrl } /${ serverId } ?error=access_denied&state=test-state` ,
371+ { method : "GET" , redirect : "manual" }
372+ )
373+ ) ;
374+
375+ expect ( response . status ) . toBe ( 302 ) ;
376+ expect ( response . headers . get ( "Location" ) ) . toMatch (
377+ / ^ h t t p : \/ \/ e x a m p l e \. c o m \/ e r r o r \? e r r o r = /
378+ ) ;
379+ } ) ;
380+ } ) ;
293381} ) ;
0 commit comments