@@ -269,3 +269,96 @@ public function createAuthorizationCodeGrantClient(string $name, array $redirect
269269
270270 $ response ->assertStatus (422 );
271271});
272+
273+ it ('handles oauth discovery with multi-segment paths ' , function (): void {
274+ // Fake Passport's routes so the oauthRoutes() helper can resolve them.
275+ Route::get ('/oauth/authorize ' )->name ('passport.authorizations.authorize ' );
276+ Route::post ('/oauth/token ' )->name ('passport.token ' );
277+
278+ $ registrar = new Registrar ;
279+ $ registrar ->oauthRoutes ();
280+
281+ // Test protected resource endpoint with multi-segment path
282+ $ response = $ this ->getJson ('/.well-known/oauth-protected-resource/mcp/weather ' );
283+
284+ $ response ->assertStatus (200 );
285+ $ response ->assertJson ([
286+ 'resource ' => url ('/mcp/weather ' ),
287+ 'authorization_servers ' => [url ('/mcp/weather ' )],
288+ 'scopes_supported ' => ['mcp:use ' ],
289+ ]);
290+
291+ // Test authorization server endpoint with multi-segment path
292+ $ response = $ this ->getJson ('/.well-known/oauth-authorization-server/mcp/weather ' );
293+
294+ $ response ->assertStatus (200 );
295+ $ response ->assertJsonStructure ([
296+ 'issuer ' ,
297+ 'authorization_endpoint ' ,
298+ 'token_endpoint ' ,
299+ 'registration_endpoint ' ,
300+ 'response_types_supported ' ,
301+ 'code_challenge_methods_supported ' ,
302+ 'scopes_supported ' ,
303+ 'grant_types_supported ' ,
304+ ]);
305+ $ response ->assertJson ([
306+ 'issuer ' => url ('/mcp/weather ' ),
307+ 'scopes_supported ' => ['mcp:use ' ],
308+ 'response_types_supported ' => ['code ' ],
309+ 'code_challenge_methods_supported ' => ['S256 ' ],
310+ 'grant_types_supported ' => ['authorization_code ' , 'refresh_token ' ],
311+ ]);
312+ });
313+
314+ it ('handles oauth discovery with single segment paths ' , function (): void {
315+ // Fake Passport's routes so the oauthRoutes() helper can resolve them.
316+ Route::get ('/oauth/authorize ' )->name ('passport.authorizations.authorize ' );
317+ Route::post ('/oauth/token ' )->name ('passport.token ' );
318+
319+ $ registrar = new Registrar ;
320+ $ registrar ->oauthRoutes ();
321+
322+ // Test backward compatibility with single-segment paths
323+ $ response = $ this ->getJson ('/.well-known/oauth-protected-resource/mcp ' );
324+
325+ $ response ->assertStatus (200 );
326+ $ response ->assertJson ([
327+ 'resource ' => url ('/mcp ' ),
328+ 'authorization_servers ' => [url ('/mcp ' )],
329+ 'scopes_supported ' => ['mcp:use ' ],
330+ ]);
331+
332+ $ response = $ this ->getJson ('/.well-known/oauth-authorization-server/mcp ' );
333+
334+ $ response ->assertStatus (200 );
335+ $ response ->assertJson ([
336+ 'issuer ' => url ('/mcp ' ),
337+ ]);
338+ });
339+
340+ it ('handles oauth discovery with no path ' , function (): void {
341+ // Fake Passport's routes so the oauthRoutes() helper can resolve them.
342+ Route::get ('/oauth/authorize ' )->name ('passport.authorizations.authorize ' );
343+ Route::post ('/oauth/token ' )->name ('passport.token ' );
344+
345+ $ registrar = new Registrar ;
346+ $ registrar ->oauthRoutes ();
347+
348+ // Test with no path (root)
349+ $ response = $ this ->getJson ('/.well-known/oauth-protected-resource ' );
350+
351+ $ response ->assertStatus (200 );
352+ $ response ->assertJson ([
353+ 'resource ' => url ('/ ' ),
354+ 'authorization_servers ' => [url ('/ ' )],
355+ 'scopes_supported ' => ['mcp:use ' ],
356+ ]);
357+
358+ $ response = $ this ->getJson ('/.well-known/oauth-authorization-server ' );
359+
360+ $ response ->assertStatus (200 );
361+ $ response ->assertJson ([
362+ 'issuer ' => url ('/ ' ),
363+ ]);
364+ });
0 commit comments