Skip to content

Commit 6b768d8

Browse files
committed
Cleanup
1 parent 4cff084 commit 6b768d8

6 files changed

Lines changed: 95 additions & 138 deletions

File tree

src/ModelContextProtocol.AspNetCore/Auth/McpAuthenticationHandler.cs

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@
22
using Microsoft.AspNetCore.Http;
33
using Microsoft.Extensions.Logging;
44
using Microsoft.Extensions.Options;
5+
using ModelContextProtocol.Auth.Types;
6+
using ModelContextProtocol.Utils.Json;
57
using System.Text.Encodings.Web;
68

79
namespace ModelContextProtocol.AspNetCore.Auth;
810

911
/// <summary>
10-
/// Authentication handler for MCP protocol that adds resource metadata to challenge responses.
12+
/// Authentication handler for MCP protocol that adds resource metadata to challenge responses
13+
/// and handles resource metadata endpoint requests.
1114
/// </summary>
12-
public class McpAuthenticationHandler : AuthenticationHandler<McpAuthenticationOptions>
15+
public class McpAuthenticationHandler : AuthenticationHandler<McpAuthenticationOptions>, IAuthenticationRequestHandler
1316
{
17+
private readonly IOptionsMonitor<McpAuthenticationOptions> _optionsMonitor;
18+
1419
/// <summary>
1520
/// Initializes a new instance of the <see cref="McpAuthenticationHandler"/> class.
1621
/// </summary>
@@ -20,6 +25,60 @@ public McpAuthenticationHandler(
2025
UrlEncoder encoder)
2126
: base(options, logger, encoder)
2227
{
28+
_optionsMonitor = options;
29+
}
30+
31+
/// <inheritdoc />
32+
public async Task<bool> HandleRequestAsync()
33+
{
34+
// Check if the request is for the resource metadata endpoint
35+
string requestPath = Request.Path.Value ?? string.Empty;
36+
var options = _optionsMonitor.CurrentValue;
37+
string resourceMetadataPath = options.ResourceMetadataUri.ToString();
38+
39+
// If the path doesn't match, let the request continue through the pipeline
40+
if (!string.Equals(requestPath, resourceMetadataPath, StringComparison.OrdinalIgnoreCase))
41+
{
42+
return false;
43+
}
44+
45+
// This is a request for resource metadata - handle it
46+
await HandleResourceMetadataRequestAsync();
47+
return true;
48+
}
49+
50+
/// <summary>
51+
/// Handles the resource metadata request.
52+
/// </summary>
53+
private async Task HandleResourceMetadataRequestAsync()
54+
{
55+
// Get a copy of the resource metadata from options to avoid modifying the original
56+
var options = _optionsMonitor.CurrentValue;
57+
var metadata = new ProtectedResourceMetadata
58+
{
59+
AuthorizationServers = [.. options.ResourceMetadata.AuthorizationServers],
60+
BearerMethodsSupported = [.. options.ResourceMetadata.BearerMethodsSupported],
61+
ScopesSupported = [.. options.ResourceMetadata.ScopesSupported],
62+
ResourceDocumentation = options.ResourceMetadata.ResourceDocumentation
63+
};
64+
65+
// Set default resource if not set
66+
if (metadata.Resource == null)
67+
{
68+
var request = Request;
69+
var hostString = request.Host.Value;
70+
var scheme = request.Scheme;
71+
metadata.Resource = new Uri($"{scheme}://{hostString}");
72+
}
73+
74+
Response.StatusCode = StatusCodes.Status200OK;
75+
Response.ContentType = "application/json";
76+
77+
var json = System.Text.Json.JsonSerializer.Serialize(
78+
metadata,
79+
McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ProtectedResourceMetadata)));
80+
81+
await Response.WriteAsync(json);
2382
}
2483

2584
/// <inheritdoc />
@@ -36,18 +95,29 @@ protected override Task HandleChallengeAsync(AuthenticationProperties properties
3695
// Set the response status code
3796
Response.StatusCode = StatusCodes.Status401Unauthorized;
3897

98+
// Get the current options to ensure we have the latest values
99+
var options = _optionsMonitor.CurrentValue;
100+
39101
// Generate the full resource metadata URL based on the current request
40102
var baseUrl = $"{Request.Scheme}://{Request.Host}";
41103

42-
// Properly parse and validate the ResourceMetadataUri
43-
if (!Uri.TryCreate(Options.ResourceMetadataUri.ToString(), UriKind.Absolute, out var prmDocumentUri))
44-
throw new InvalidOperationException("Invalid ResourceMetadataUri in options.");
45-
46-
// Verify that the URI scheme starts with "http"
47-
if (!prmDocumentUri.Scheme.StartsWith("http", StringComparison.OrdinalIgnoreCase))
48-
throw new InvalidOperationException("ResourceMetadataUri must use HTTP or HTTPS scheme.");
49-
50-
var rawPrmDocumentUri = prmDocumentUri.ToString();
104+
string resourceMetadataUriString = options.ResourceMetadataUri.ToString();
105+
string rawPrmDocumentUri;
106+
107+
// Check if the URI is relative or absolute
108+
if (options.ResourceMetadataUri.IsAbsoluteUri)
109+
{
110+
rawPrmDocumentUri = resourceMetadataUriString;
111+
}
112+
else
113+
{
114+
// For relative URIs, combine with the base URL
115+
if (!Uri.TryCreate(baseUrl + resourceMetadataUriString, UriKind.Absolute, out var absoluteUri))
116+
{
117+
throw new InvalidOperationException("Could not create absolute URI for resource metadata.");
118+
}
119+
rawPrmDocumentUri = absoluteUri.ToString();
120+
}
51121

52122
// Initialize properties if null
53123
properties ??= new AuthenticationProperties();

src/ModelContextProtocol.AspNetCore/Auth/McpAuthorizationExtensions.cs

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,21 @@ public static AuthenticationBuilder AddMcp(
4040
string displayName,
4141
Action<McpAuthenticationOptions>? configureOptions = null)
4242
{
43-
// Create options instance to pass to ResourceMetadataService
44-
var options = new McpAuthenticationOptions();
45-
configureOptions?.Invoke(options);
46-
47-
// Register ResourceMetadataService with options
48-
builder.Services.AddSingleton(sp => {
49-
var service = new ResourceMetadataService();
50-
51-
// Configure the service with the resource metadata from options
52-
service.ConfigureMetadata(metadata => {
53-
metadata.Resource = options.ResourceMetadata.Resource;
54-
metadata.AuthorizationServers = options.ResourceMetadata.AuthorizationServers;
55-
metadata.BearerMethodsSupported = options.ResourceMetadata.BearerMethodsSupported;
56-
metadata.ScopesSupported = options.ResourceMetadata.ScopesSupported;
57-
metadata.ResourceDocumentation = options.ResourceMetadata.ResourceDocumentation;
58-
});
59-
60-
return service;
61-
});
62-
63-
builder.Services.TryAddSingleton<McpAuthorizationMarker>();
43+
if (configureOptions != null)
44+
{
45+
if (authenticationScheme == McpAuthenticationDefaults.AuthenticationScheme)
46+
{
47+
builder.Services.Configure(configureOptions);
48+
}
49+
else
50+
{
51+
builder.Services.Configure(authenticationScheme, configureOptions);
52+
}
53+
}
6454

6555
return builder.AddScheme<McpAuthenticationOptions, McpAuthenticationHandler>(
66-
authenticationScheme,
67-
displayName,
68-
opt => {
69-
if (configureOptions != null)
70-
{
71-
configureOptions(opt);
72-
}
73-
});
56+
authenticationScheme,
57+
displayName,
58+
options => { }); // No-op to avoid overriding
7459
}
7560
}

src/ModelContextProtocol.AspNetCore/Auth/McpAuthorizationMarker.cs

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

src/ModelContextProtocol.AspNetCore/Auth/ResourceMetadataEndpointHandler.cs

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

src/ModelContextProtocol.AspNetCore/Auth/ResourceMetadataService.cs

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

src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,6 @@ public static IEndpointConventionBuilder MapMcp(this IEndpointRouteBuilder endpo
5252
.WithMetadata(new AcceptsMetadata(["application/json"]))
5353
.WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted));
5454

55-
// Check if authentication/authorization is configured
56-
var authMarker = endpoints.ServiceProvider.GetService<McpAuthorizationMarker>();
57-
if (authMarker != null)
58-
{
59-
// Authorization is configured, so automatically map the OAuth protected resource endpoint
60-
var resourceMetadataService = endpoints.ServiceProvider.GetRequiredService<ResourceMetadataService>();
61-
62-
var handler = new ResourceMetadataEndpointHandler(resourceMetadataService);
63-
64-
var options = endpoints.ServiceProvider.GetRequiredService<IOptions<McpAuthenticationOptions>>();
65-
var metadataPath = options.Value.ResourceMetadataUri.ToString();
66-
67-
sseGroup.MapGet(metadataPath, handler.HandleRequest)
68-
.WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, contentTypes: ["application/json"]))
69-
.AllowAnonymous()
70-
.WithDisplayName("MCP Resource Metadata");
71-
}
72-
7355
return mcpGroup;
7456
}
7557
}

0 commit comments

Comments
 (0)