Add auth regression test for missing resource in PRM (#1265)

This commit is contained in:
Copilot 2026-02-13 09:26:00 -08:00 • committed by GitHub
commit b8ae4fec2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 2 deletions

View file

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol;
using ModelContextProtocol.AspNetCore.Authentication;
using ModelContextProtocol.Authentication;
using ModelContextProtocol.Client;
@ -528,6 +529,47 @@ public class AuthTests : OAuthTestBase
transport, loggerFactory: LoggerFactory, cancellationToken: TestContext.Current.CancellationToken));
}
[Fact]
public async Task CannotAuthenticate_WhenProtectedResourceMetadataMissingResource()
{
TestOAuthServer.RequireResource = false;
Builder.Services.Configure<McpAuthenticationOptions>(McpAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Events.OnResourceMetadataRequest = async context =>
{
context.HandleResponse();
var metadata = new ProtectedResourceMetadata
{
AuthorizationServers = { new Uri(OAuthServerUrl) },
ScopesSupported = ["mcp:tools"],
};
await Results.Json(metadata, McpJsonUtilities.DefaultOptions).ExecuteAsync(context.HttpContext);
};
});
await using var app = await StartMcpServerAsync();
await using var transport = new HttpClientTransport(new()
{
Endpoint = new(McpServerUrl),
OAuth = new()
{
ClientId = "demo-client",
ClientSecret = "demo-secret",
RedirectUri = new Uri("http://localhost:1179/callback"),
AuthorizationRedirectDelegate = HandleAuthorizationUrlAsync,
},
}, HttpClient, LoggerFactory);
var ex = await Assert.ThrowsAsync<McpException>(() => McpClient.CreateAsync(
transport, loggerFactory: LoggerFactory, cancellationToken: TestContext.Current.CancellationToken));
Assert.Contains("Resource URI in metadata", ex.Message);
}
[Fact]
public async Task CanAuthenticate_WithAuthorizationServerPathInsertionMetadata()
{

View file

@ -67,6 +67,14 @@ public sealed class Program
/// </remarks>
public bool ClientIdMetadataDocumentSupported { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether the authorization server requires a resource parameter.
/// </summary>
/// <remarks>
/// The default value is <c>true</c>.
/// </remarks>
public bool RequireResource { get; set; } = true;
public HashSet<string> DisabledMetadataPaths { get; } = new(StringComparer.OrdinalIgnoreCase);
public IReadOnlyCollection<string> MetadataRequests => _metadataRequests.ToArray();
@ -290,7 +298,7 @@ public sealed class Program
}
// Validate resource in accordance with RFC 8707
if (string.IsNullOrEmpty(resource) || !ValidResources.Contains(resource))
if (RequireResource && (string.IsNullOrEmpty(resource) || !ValidResources.Contains(resource)))
{
return Results.Redirect($"{redirect_uri}?error=invalid_target&error_description=The+specified+resource+is+not+valid&state={state}");
}
@ -338,7 +346,7 @@ public sealed class Program
// Validate resource in accordance with RFC 8707
var resource = form["resource"].ToString();
if (string.IsNullOrEmpty(resource) || !ValidResources.Contains(resource))
if (RequireResource && (string.IsNullOrEmpty(resource) || !ValidResources.Contains(resource)))
{
return Results.BadRequest(new OAuthErrorResponse
{