Skip to content

Fix: 401 Unauthorized on all [Authorize]-protected MCP tools due to premature authentication check#3

Open
alealoisi-nts wants to merge 3 commits intokaladinstorm84:mainfrom
alealoisi-nts:main
Open

Fix: 401 Unauthorized on all [Authorize]-protected MCP tools due to premature authentication check#3
alealoisi-nts wants to merge 3 commits intokaladinstorm84:mainfrom
alealoisi-nts:main

Conversation

@alealoisi-nts
Copy link
Copy Markdown

Problem

When an MCP client calls a tool mapped to a controller action protected by
[Authorize], the dispatcher always returns 401 Unauthorized — even when
valid credentials are present in the Authorization header.

Root Cause

McpToolDispatcher.DispatchAsync() contained an early authentication guard:

if (RequiresAuthentication(descriptor) && !IsAuthenticated(context))
    return DispatchResult.Failure(401, "Unauthorized");

This check evaluates context.User on the freshly built synthetic HttpContext
before invoker.InvokeAsync() is called. Since authentication runs as part
of the ASP.NET Core filter pipeline (inside InvokeAsync), the User principal
is always empty at this point — causing every protected tool to be rejected
regardless of the credentials provided.

The Authorization header forwarding in SyntheticHttpContextFactory was
already working correctly — the header is present on the synthetic request,
but the early check prevented the pipeline from ever reading it.

Fix

  • Removed the RequiresAuthentication / IsAuthenticated guard block from
    DispatchAsync and their two helper methods.
  • Authentication is now handled entirely by the ASP.NET Core action invoker
    pipeline, which correctly reads the forwarded Authorization header and
    authenticates the user before the action executes.

Testing

Verified with an ASP.NET Core API using [Authorize(AuthenticationSchemes = "Basic")]
at controller level. After this fix, MCP tool calls with a valid Authorization: Basic
header are correctly authenticated and return 200 OK.

…P tools

**Why:** The `Authorization` header is already correctly forwarded from the MCP request to the synthetic `HttpContext` by `SyntheticHttpContextFactory`. However, the early auth check evaluates `context.User` before `invoker.InvokeAsync()` has run — meaning authentication has never been executed yet and `User` is always empty. The check therefore always returns 401 for any protected endpoint, regardless of whether valid credentials were provided. Removing it lets the full ASP.NET Core filter pipeline run normally, which correctly authenticates the user from the forwarded header.
@kaladinstorm84 kaladinstorm84 self-requested a review February 28, 2026 09:31
@kaladinstorm84
Copy link
Copy Markdown
Owner

kaladinstorm84 commented Feb 28, 2026

Could you please add this change to zeroMcp/zeromcp.net as this repo has been migrated and is in the process of being wound down.

I would migrate the changes over myself, but would like you to maintain the credit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants