Skip to content

Commit bdea276

Browse files
committed
Fix OAuth Protected Resource Metadata flows with AZURE_MCP_DANGEROUSLY_ENABLE_FORWARDED_HEADERS. This configuration will read the X-Forwarded-Proto request header sent from Azure Container Apps to construct the URLs needed for OAuth PRM within the original claims challenge and the PRM endpoint's JSON body.
1 parent 842d6be commit bdea276

File tree

1 file changed

+50
-2
lines changed

1 file changed

+50
-2
lines changed

core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.CommandLine.Parsing;
55
using System.Diagnostics;
66
using System.Net;
7+
using Azure.Core;
78
using Azure.Mcp.Core.Areas.Server.Models;
89
using Azure.Mcp.Core.Areas.Server.Options;
910
using Azure.Mcp.Core.Commands;
@@ -21,6 +22,7 @@
2122
using Microsoft.Extensions.Hosting;
2223
using Microsoft.Extensions.Logging;
2324
using Microsoft.Extensions.Options;
25+
using Microsoft.Extensions.Primitives;
2426
using Microsoft.Identity.Web;
2527
using OpenTelemetry;
2628
using OpenTelemetry.Logs;
@@ -414,7 +416,8 @@ private IHost CreateHttpHost(ServiceStartOptions serverOptions)
414416
if (!context.Response.HasStarted)
415417
{
416418
HttpRequest request = context.Request;
417-
string resourceMetadataUrl = $"{request.Scheme}://{request.Host}/.well-known/oauth-protected-resource";
419+
string scheme = GetSchemeForOAuthProtectedResourceMetadata(request);
420+
string resourceMetadataUrl = $"{scheme}://{request.Host}/.well-known/oauth-protected-resource";
418421

419422
// Modify the WWW-Authenticate header to include resource_metadata
420423
context.Response.Headers.WWWAuthenticate =
@@ -498,7 +501,8 @@ private IHost CreateHttpHost(ServiceStartOptions serverOptions)
498501
.GetRequiredService<IOptionsMonitor<MicrosoftIdentityOptions>>();
499502
MicrosoftIdentityOptions azureAdOptions = azureAdOptionsMonitor.Get(JwtBearerDefaults.AuthenticationScheme);
500503
HttpRequest request = context.Request;
501-
string baseUrl = $"{request.Scheme}://{request.Host}";
504+
string scheme = GetSchemeForOAuthProtectedResourceMetadata(request);
505+
string baseUrl = $"{scheme}://{request.Host}";
502506
string? clientId = azureAdOptions.ClientId;
503507
string? tenantId = azureAdOptions.TenantId;
504508
string instance = azureAdOptions.Instance?.TrimEnd('/') ?? "https://login.microsoftonline.com";
@@ -552,6 +556,50 @@ await JsonSerializer.SerializeAsync(
552556
.AllowAnonymous();
553557

554558
return app;
559+
560+
string GetSchemeForOAuthProtectedResourceMetadata(HttpRequest request)
561+
{
562+
string scheme = request.Scheme;
563+
564+
// Default to "false" for enabling forwarded headers. The env var must be present,
565+
// and it must be parsed to "true".
566+
bool enableForwardedHeaders =
567+
bool.TryParse(
568+
Environment.GetEnvironmentVariable("AZURE_MCP_DANGEROUSLY_ENABLE_FORWARDED_HEADERS"),
569+
out bool parsedEnvVar)
570+
&& parsedEnvVar;
571+
572+
// Azure Container Apps setups usually use HTTP between the ACA platform's
573+
// reverse proxy and the application container. Our OAuth claims challenge
574+
// needs to match what the client will use as a scheme. So only in this
575+
// case do we use the X-Forwarded-Proto header if present. We're also going
576+
// to limit specifically to "http" and "https" values and use their
577+
// lowercase forms rather than the casing in the header.
578+
//
579+
// Other reverse proxies or load balancers may also use X-Forwarded-Proto or
580+
// may use something different. We only special case ACA here because it's
581+
// part of the samples as of 2.0-beta.5. More thorough logic and any
582+
// configuration options can be added later if needed, and that could use
583+
// ASP.NET Core's Forwarded Headers Middleware. See:
584+
// https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer
585+
if (enableForwardedHeaders
586+
&& request.Headers.TryGetValue("X-Forwarded-Proto", out StringValues forwardedProto))
587+
{
588+
if (forwardedProto.FirstOrDefault() is string forwardedProtoValue)
589+
{
590+
if (string.Equals(forwardedProtoValue, "https", StringComparison.OrdinalIgnoreCase))
591+
{
592+
scheme = "https";
593+
}
594+
else if (string.Equals(forwardedProtoValue, "http", StringComparison.OrdinalIgnoreCase))
595+
{
596+
scheme = "http";
597+
}
598+
}
599+
}
600+
601+
return scheme;
602+
}
555603
}
556604

557605
/// <summary>

0 commit comments

Comments
 (0)