Skip to content

Commit 991b29b

Browse files
committed
Simplify the handling of the WWW-Authenticate implementaiton
1 parent 48bff9c commit 991b29b

File tree

6 files changed

+56
-118
lines changed

6 files changed

+56
-118
lines changed

samples/ProtectedMCPServer/Program.cs

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,23 @@
7676
metadata.ResourceDocumentation = new Uri("https://docs.example.com/api/weather");
7777
});
7878

79-
// Configure authentication using the built-in authentication system
80-
builder.Services.AddAuthentication(options =>
81-
{
82-
options.DefaultScheme = "Bearer";
83-
options.DefaultChallengeScheme = "Bearer"; // Ensure challenges use Bearer scheme
84-
})
85-
.AddScheme<AuthenticationSchemeOptions, SimpleAuthHandler>("Bearer", options => { });
86-
87-
// Add authorization policy for MCP
88-
builder.Services.AddAuthorization(options =>
89-
{
90-
options.AddPolicy("McpAuth", policy =>
91-
{
92-
policy.RequireAuthenticatedUser();
93-
policy.RequireClaim("scope", "weather.read");
94-
});
95-
});
79+
// // Configure authentication using the built-in authentication system
80+
// builder.Services.AddAuthentication(options =>
81+
// {
82+
// options.DefaultScheme = "Bearer";
83+
// options.DefaultChallengeScheme = "Bearer"; // Ensure challenges use Bearer scheme
84+
// })
85+
// .AddScheme<AuthenticationSchemeOptions, SimpleAuthHandler>("Bearer", options => { });
86+
87+
//// Add authorization policy for MCP
88+
//builder.Services.AddAuthorization(options =>
89+
//{
90+
// options.AddPolicy("McpAuth", policy =>
91+
// {
92+
// policy.RequireAuthenticatedUser();
93+
// policy.RequireClaim("scope", "weather.read");
94+
// });
95+
//});
9696

9797
var app = builder.Build();
9898

@@ -173,8 +173,6 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
173173

174174
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
175175
{
176-
// No need to manually set WWW-Authenticate header anymore - handled by middleware
177-
Response.StatusCode = 401;
178-
return Task.CompletedTask;
176+
return base.HandleChallengeAsync(properties);
179177
}
180178
}

src/ModelContextProtocol.AspNetCore/Auth/McpAuthenticationHandler.cs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using Microsoft.AspNetCore.Authentication;
22
using Microsoft.Extensions.Logging;
33
using Microsoft.Extensions.Options;
4+
using System;
45
using System.Text.Encodings.Web;
6+
using System.Threading.Tasks;
57

68
namespace ModelContextProtocol.AspNetCore.Auth;
79

@@ -10,15 +12,19 @@ namespace ModelContextProtocol.AspNetCore.Auth;
1012
/// </summary>
1113
public class McpAuthenticationHandler : AuthenticationHandler<McpAuthenticationOptions>
1214
{
15+
private readonly ResourceMetadataService _resourceMetadataService;
16+
1317
/// <summary>
1418
/// Initializes a new instance of the <see cref="McpAuthenticationHandler"/> class.
1519
/// </summary>
1620
public McpAuthenticationHandler(
1721
IOptionsMonitor<McpAuthenticationOptions> options,
1822
ILoggerFactory logger,
19-
UrlEncoder encoder)
23+
UrlEncoder encoder,
24+
ResourceMetadataService resourceMetadataService)
2025
: base(options, logger, encoder)
2126
{
27+
_resourceMetadataService = resourceMetadataService;
2228
}
2329

2430
/// <inheritdoc />
@@ -32,12 +38,30 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
3238
/// <inheritdoc />
3339
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
3440
{
35-
if (Options.ResourceMetadataUri != null)
36-
{
37-
Response.Headers.WWWAuthenticate = $"Bearer resource_metadata=\"{Options.ResourceMetadataUri}\"";
38-
}
41+
// Set the response status code
42+
Response.StatusCode = 401; // Unauthorized
43+
44+
// Generate the full resource metadata URL based on the current request
45+
var baseUrl = $"{Request.Scheme}://{Request.Host}";
46+
var metadataPath = Options.ResourceMetadataUri?.ToString() ?? "/.well-known/oauth-protected-resource";
47+
var metadataUrl = metadataPath.StartsWith("http", StringComparison.OrdinalIgnoreCase)
48+
? metadataPath
49+
: $"{baseUrl}{metadataPath}";
50+
51+
// Initialize properties if null
52+
properties ??= new AuthenticationProperties();
53+
54+
// Set the WWW-Authenticate header with the resource_metadata
55+
string headerValue = $"Bearer realm=\"{Scheme.Name}\"";
56+
headerValue += $", resource_metadata=\"{metadataUrl}\"";
57+
58+
// Use Headers.Append with a StringValues object
59+
Response.Headers["WWW-Authenticate"] = headerValue;
60+
61+
// Store the resource_metadata in properties in case other handlers need it
62+
properties.Items["resource_metadata"] = metadataUrl;
3963

40-
return Task.CompletedTask;
64+
return base.HandleChallengeAsync(properties);
4165
}
4266
}
4367

src/ModelContextProtocol.AspNetCore/Auth/McpAuthenticationResponseMiddleware.cs

Lines changed: 0 additions & 57 deletions
This file was deleted.

src/ModelContextProtocol.AspNetCore/Auth/McpAuthenticationResponseMiddlewareExtensions.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/ModelContextProtocol.AspNetCore/Auth/McpWebApplicationExtensions.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ namespace ModelContextProtocol.AspNetCore.Auth;
99
public static class McpWebApplicationExtensions
1010
{
1111
/// <summary>
12-
/// Adds the MCP authentication response middleware to the application pipeline.
13-
/// This middleware automatically adds the resource_metadata field to WWW-Authenticate headers in 401 responses.
12+
/// This method maintains compatibility with existing code that calls UseMcpAuthenticationResponse.
13+
/// The actual middleware functionality is now handled by the McpAuthenticationHandler as part of
14+
/// the standard ASP.NET Core authentication pipeline.
1415
/// </summary>
1516
/// <remarks>
16-
/// This middleware should be registered AFTER UseAuthentication() but BEFORE UseAuthorization().
17+
/// While this method is still required for backward compatibility, its functionality
18+
/// is now fully implemented by the McpAuthenticationHandler.
1719
/// </remarks>
1820
/// <param name="app">The web application.</param>
1921
/// <returns>The web application for chaining.</returns>
@@ -26,6 +28,8 @@ public static IApplicationBuilder UseMcpAuthenticationResponse(this IApplication
2628
"Make sure you call AddMcpServer().WithAuthorization() first.");
2729
}
2830

29-
return app.UseMiddleware<McpAuthenticationResponseMiddleware>();
31+
// Return the app directly without adding middleware, as the functionality
32+
// is now provided by the McpAuthenticationHandler
33+
return app;
3034
}
3135
}

src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,6 @@ public static IEndpointConventionBuilder MapMcp(this IEndpointRouteBuilder endpo
5555
var authMarker = endpoints.ServiceProvider.GetService<McpAuthorizationMarker>();
5656
if (authMarker != null)
5757
{
58-
// Check if the authentication response middleware is configured
59-
var authResponseMarker = endpoints.ServiceProvider.GetService<McpAuthenticationResponseMarker>();
60-
if (authResponseMarker != null)
61-
{
62-
// Register the middleware to automatically add WWW-Authenticate headers to 401 responses
63-
// We need to add this middleware to the parent app, not just the endpoint group
64-
if (endpoints is IApplicationBuilder app)
65-
{
66-
app.UseWwwAuthenticateHeaderMiddleware();
67-
}
68-
}
69-
7058
// Authorization is configured, so automatically map the OAuth protected resource endpoint
7159
var resourceMetadataService = endpoints.ServiceProvider.GetRequiredService<ResourceMetadataService>();
7260

0 commit comments

Comments
 (0)