diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs index 91bd5fc168..7605d7296b 100644 --- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs @@ -523,18 +523,10 @@ private IHost CreateHttpHost(ServiceStartOptions serverOptions) services.AddHealthChecks(); // Configure CORS - // We're allowing all origins, methods, and headers to support any web - // browser clients. + // By default in development mode, we restrict to localhost origins for security. + // In production (authenticated mode), allow configured origins or all origins if specified. // Non-browser clients are unaffected by CORS. - services.AddCors(options => - { - options.AddPolicy("AllowAll", policy => - { - policy.AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader(); - }); - }); + ConfigureCors(services, serverOptions); // Configure services ConfigureServices(services); // Our static callback hook @@ -545,7 +537,7 @@ private IHost CreateHttpHost(ServiceStartOptions serverOptions) UseHttpsRedirectionIfEnabled(app); // Configure middleware pipeline - app.UseCors("AllowAll"); + app.UseCors("McpCorsPolicy"); app.UseRouting(); // Add OAuth protected resource metadata middleware @@ -640,18 +632,10 @@ private IHost CreateIncomingAuthDisabledHttpHost(ServiceStartOptions serverOptio services.AddSingleIdentityTokenCredentialProvider(); // Configure CORS - // We're allowing all origins, methods, and headers to support any web - // browser clients. + // By default in development mode, we restrict to localhost origins for security. + // In production (authenticated mode), allow configured origins or all origins if specified. // Non-browser clients are unaffected by CORS. - services.AddCors(options => - { - options.AddPolicy("AllowAll", policy => - { - policy.AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader(); - }); - }); + ConfigureCors(services, serverOptions); // Configure services ConfigureServices(services); // Our static callback hook @@ -668,13 +652,65 @@ private IHost CreateIncomingAuthDisabledHttpHost(ServiceStartOptions serverOptio UseHttpsRedirectionIfEnabled(app); // Configure middleware pipeline - app.UseCors("AllowAll"); + app.UseCors("McpCorsPolicy"); app.UseRouting(); app.MapMcp(); return app; } + /// + /// Configures CORS policy based on environment and configuration. + /// In development mode (unauthenticated), restricts to localhost for security. + /// In production (authenticated), allows all origins (safe due to authentication requirement). + /// + /// The service collection to configure. + /// The server configuration options. + private static void ConfigureCors(IServiceCollection services, ServiceStartOptions serverOptions) + { + services.AddCors(options => + { + options.AddPolicy("McpCorsPolicy", policy => + { + // In unauthenticated mode (development), restrict to localhost by default + // Allows localhost with any port to support various development scenarios: + // - Port 1031: Default HTTP mode (launchSettings.json) + // - Port 5008: SSE mode default + // - Port 5173: MCP Inspector tool + // - Custom ports: User-specified via ASPNETCORE_URLS + if (serverOptions.DangerouslyDisableHttpIncomingAuth) + { + policy.SetIsOriginAllowed(origin => + { + if (Uri.TryCreate(origin, UriKind.Absolute, out var uri)) + { + // Allow localhost and 127.0.0.1 with any port + return uri.Host == "localhost" || + uri.Host == "127.0.0.1" || + uri.Host == "[::1]"; // IPv6 loopback + } + return false; + }) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); // Required when using SetIsOriginAllowed + } + // In authenticated mode (production), allow all origins by default + // This is safe because: + // 1. Authentication (JWT Bearer) validates all requests regardless of origin + // 2. CORS is a browser security mechanism, not a server security feature + // 3. MCP clients (GitHub Copilot in VS Code/Codespaces) need to connect from various origins + // 4. The server still enforces authentication and authorization on every request + else + { + policy.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + } + }); + }); + } + /// /// Configures the MCP server services. /// @@ -852,11 +888,11 @@ private static WebApplication UseHttpsRedirectionIfEnabled(WebApplication app) } return Sdk.CreateTracerProviderBuilder() - // captures incoming HTTP requests .AddAspNetCoreInstrumentation() - // captures outgoing HTTP requests with filtering - .AddHttpClientInstrumentation(o => o.FilterHttpRequestMessage = ShouldInstrumentHttpRequest) - .AddAzureMonitorTraceExporter(exporterOptions => exporterOptions.ConnectionString = connectionString) + .AddHttpClientInstrumentation(o => + o.FilterHttpRequestMessage = ShouldInstrumentHttpRequest) + .AddAzureMonitorTraceExporter(exporterOptions => + exporterOptions.ConnectionString = connectionString) .Build(); } diff --git a/servers/Azure.Mcp.Server/changelog-entries/1770078551923.yaml b/servers/Azure.Mcp.Server/changelog-entries/1770078551923.yaml new file mode 100644 index 0000000000..4561c87047 --- /dev/null +++ b/servers/Azure.Mcp.Server/changelog-entries/1770078551923.yaml @@ -0,0 +1,3 @@ +changes: + - section: "Bugs Fixed" + description: "Added CORS policy to restrict cross-origin requests to localhost when running in unauthenticated development environment" \ No newline at end of file