McpAuthZ.AspNetCore
ASP.NET Core middleware for MCP HTTP server OAuth authorization. Implements RFC 9728 (Protected Resource Metadata), RFC 6750 (Bearer Token Challenges), and RFC 8707 (Audience Validation) with scope-based authorization, reverse proxy support, and multi-tenant named options.
Ask AI about McpAuthZ.AspNetCore
Powered by Claude · Grounded in docs
I know everything about McpAuthZ.AspNetCore. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
McpAuthZ.AspNetCore
ASP.NET Core library for MCP HTTP server OAuth authorization, implementing RFC 9728 (Protected Resource Metadata), RFC 6750 (Bearer Token Usage), and RFC 8707 (Resource Indicators) per the MCP specification (2025-11-25).
Installation
<PackageReference Include="McpAuthZ.AspNetCore" Version="1.0.0" />
Quick Start
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => { /* configure your IdP */ });
builder.Services.AddMcpAuthZ(options =>
{
options.Resource = new Uri("https://example.com/api/mcp");
options.AuthorizationServers = [new Uri("https://auth.example.com")];
options.McpEndpointPaths = [new PathString("/api/mcp")];
options.AuthenticationScheme = "Bearer";
});
var app = builder.Build();
app.MapMcpProtectedResourceMetadata(); // Serves /.well-known/oauth-protected-resource/...
app.UseMcpAuthZ(); // Enforces auth on MCP endpoints
app.MapPost("/api/mcp", (HttpContext ctx) => Results.Ok());
app.Run();
Samples
| Sample | Description |
|---|---|
| McpAuthZ.QuickStart | Zero-dependency local dev with dotnet user-jwts — start here |
| McpAuthZ.Auth0Sample | Full Auth0 integration with audience validation and reverse proxy |
Configuration Reference
Core Properties
| Property | Type | Default | Description |
|---|---|---|---|
Resource | Uri | required | Canonical HTTPS resource URI (no fragment) |
AuthorizationServers | IReadOnlyList<Uri> | required | OAuth AS issuer URIs |
McpEndpointPaths | IReadOnlyList<PathString> | [] | Paths requiring authentication |
AuthenticationScheme | string? | null | Auth scheme name (null = default scheme) |
Realm | string | "McpAuthZ" | Realm for WWW-Authenticate challenges |
Protected Resource Metadata (PRM)
| Property | Type | Default | Description |
|---|---|---|---|
ScopesSupported | IReadOnlyList<string> | [] | Scopes advertised in PRM |
ResourceDocumentation | Uri? | null | Documentation URL in PRM |
IncludeBearerMethodsSupported | bool | true | Include bearer_methods_supported in PRM |
ResourceMetadataUriOverride | Uri? | null | Override computed PRM URL |
Reverse Proxy Support
| Property | Type | Default | Description |
|---|---|---|---|
PublicOriginOverride | Uri? | null | Public scheme+host when behind a reverse proxy |
PublicPathBaseOverride | PathString | "" | Public path prefix added by the proxy |
Scope Authorization
| Property | Type | Default | Description |
|---|---|---|---|
ConnectScopes | IReadOnlyList<string> | [] | Required scopes for GET (SSE/Streamable HTTP) |
MethodScopes | IReadOnlyDictionary<string, IReadOnlyList<string>> | {} | Required scopes per JSON-RPC method |
ToolScopes | IReadOnlyDictionary<string, IReadOnlyList<string>> | {} | Required scopes per tool name (priority over MethodScopes) |
DefaultScopes | IReadOnlyList<string> | [] | Fallback scopes for unclassified requests |
ScopeClaimTypes | IReadOnlyList<string> | ["scope", "scp"] | Claim types to search for granted scopes |
ScopeSelectionMode | ScopeSelectionMode | UnionGrantedAndRequired | How challenge scope list is computed |
IncludeScopeIn401 | bool | true | Include scope hint in 401 challenges |
Audience Validation
| Property | Type | Default | Description |
|---|---|---|---|
AllowedAudiences | IReadOnlyList<string> | [] | Additional valid audience values beyond Resource.AbsoluteUri |
EnforceAudienceValidation | bool | true | Enable audience validation (disable only for dev/migration) |
AudienceClaimTypes | IReadOnlyList<string> | ["aud"] | Claim types to search for audience values |
Reverse Proxy Setup
When your MCP server sits behind a reverse proxy (e.g., Azure App Service with path-based routing), configure the public-facing URL so that resource_metadata URIs in challenge headers point to the correct location:
builder.Services.AddMcpAuthZ(options =>
{
// The externally-reachable resource URI (must match what clients use)
options.Resource = new Uri("https://abc.azure.com/sanjayd/api/mcp");
options.AuthorizationServers = [new Uri("https://login.microsoftonline.com/tenant-id/v2.0")];
options.McpEndpointPaths = [new PathString("/api/mcp")];
// Tell the library the public origin and path prefix
options.PublicOriginOverride = new Uri("https://abc.azure.com");
options.PublicPathBaseOverride = new PathString("/sanjayd");
});
Scope Mapping
Map JSON-RPC methods and tools to required scopes:
builder.Services.AddMcpAuthZ(options =>
{
options.Resource = new Uri("https://example.com/api/mcp");
options.AuthorizationServers = [new Uri("https://auth.example.com")];
options.McpEndpointPaths = [new PathString("/api/mcp")];
// Scopes for SSE/Streamable HTTP connections
options.ConnectScopes = ["mcp:connect"];
// Scopes per JSON-RPC method
options.MethodScopes = new Dictionary<string, IReadOnlyList<string>>
{
["resources/list"] = ["resources:read"],
["prompts/list"] = ["prompts:read"]
};
// Scopes per tool name (takes priority for tools/call requests)
options.ToolScopes = new Dictionary<string, IReadOnlyList<string>>
{
["search"] = ["tools:search"],
["execute"] = ["tools:execute"]
};
// Fallback for unrecognized methods
options.DefaultScopes = ["mcp:default"];
});
Audience Validation
The library validates that tokens were issued specifically for your resource (RFC 8707). Resource.AbsoluteUri is always an implicitly valid audience value.
builder.Services.AddMcpAuthZ(options =>
{
options.Resource = new Uri("https://example.com/api/mcp");
options.AuthorizationServers = [new Uri("https://auth.example.com")];
options.McpEndpointPaths = [new PathString("/api/mcp")];
// Azure AD tokens may use api:// format as the audience
options.AllowedAudiences = ["api://my-client-id"];
// Default: true (MCP spec requires validation)
// Set to false only during development when your IdP doesn't yet emit aud claims
options.EnforceAudienceValidation = true;
});
Extensibility
All default implementations are registered via TryAddSingleton. Register your own implementation before AddMcpAuthZ() to replace any default:
// Replace the default audience validator with a custom one
builder.Services.AddSingleton<IMcpAudienceValidator, MyCustomAudienceValidator>();
// Replace the default scope reader (e.g., for non-standard claim formats)
builder.Services.AddSingleton<IMcpGrantedScopeReader, MyCustomScopeReader>();
// Then register the library (won't overwrite your registrations)
builder.Services.AddMcpAuthZ(options => { /* ... */ });
Replaceable Interfaces
| Interface | Purpose |
|---|---|
IMcpProtectedResourceMetadataProvider | PRM JSON generation |
IMcpPublicUrlProvider | Public URL computation for challenges |
IMcpAudienceValidator | Audience claim validation |
IMcpRequestClassifier | JSON-RPC request classification |
IMcpRequiredScopeResolver | Required scope resolution |
IMcpGrantedScopeReader | Granted scope extraction from claims |
IScopeSelectionStrategy | Challenge scope list computation |
Named Options (Multi-tenancy)
Serve multiple MCP endpoints with independent configurations:
builder.Services.AddMcpAuthZ("tenant-a", options =>
{
options.Resource = new Uri("https://example.com/tenant-a/mcp");
options.AuthorizationServers = [new Uri("https://auth-a.example.com")];
options.McpEndpointPaths = [new PathString("/tenant-a/mcp")];
});
builder.Services.AddMcpAuthZ("tenant-b", options =>
{
options.Resource = new Uri("https://example.com/tenant-b/mcp");
options.AuthorizationServers = [new Uri("https://auth-b.example.com")];
options.McpEndpointPaths = [new PathString("/tenant-b/mcp")];
});
var app = builder.Build();
app.MapMcpProtectedResourceMetadata("tenant-a");
app.MapMcpProtectedResourceMetadata("tenant-b");
app.UseMcpAuthZ("tenant-a");
app.UseMcpAuthZ("tenant-b");
License
See LICENSE for details.
