Skip to content

WIP: Authorization support #349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 30 commits into from
Closed

WIP: Authorization support #349

wants to merge 30 commits into from

Conversation

localden
Copy link
Collaborator

No description provided.

@localden localden marked this pull request as draft April 23, 2025 21:37
@localden
Copy link
Collaborator Author

Tracking for #156.


// Serve SSE and message endpoints with authorization
if (context.Request.Path.StartsWithSegments("/sse") ||
(context.Request.Path.Value?.EndsWith("/message") == true))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would probably be better done with endpoint filters on the RouteGroups we create in MapMcp where the routing has already been done for us. Feel free to ping me if you want any guidance on this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@halter73 can you check out the updated implementation with McpAuthorizationFilter?

@halter73
Copy link
Contributor

Just as a heads up, I opened #356 to add client-side support for the Streamable HTTP transport. I hope to get it merged quickly so people can test it out. It doesn't affect the client-side logic when the SSE transport is in use which will still be the default since that's much more widely supported.

I don't think it will affect your auth work too much.

Copy link

@DavidParks8 DavidParks8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the comments. Also, take a look at the following discussion: #135

There is already appetite in the community for using the builtin asp.net core authn/z capabilities.

ClientId = "04f79824-ab56-4511-a7cb-d7deaea92dc0",

// Setting some pre-defined scopes the client requests.
Scopes = ["User.Read"],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a usecase for this to be dynamic. I haven't looked at the rest of the code yet, but please ensure there is support for supplying the scopes dynamically in the server metadata based upon the incoming request.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DavidParks8 - this is an example. You can specify whatever scopes you want here when you establish the transport options.

$"http://{hostname}:{port}{callbackPath}"
}, // Configure the authorize callback with the same hostname, port, and path
AuthorizeCallback = AuthorizationService.CreateHttpListenerAuthorizeCallback(
openBrowser: async (url) =>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the url could be malicious. There has to be a more secure way to do this than blindly trusting what the server provides.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you hand an un‑validated string off to ShellExecute (UseShellExecute = true) you are effectively letting the user invoke any registered protocol or file association. As soon as Windows sees something like:

• mshta://evil.com/boom.hta
• ms‑msdt:/id PCWDiagnostic /skip force /path "\evil\share\payload.sps" (the “Follina” trick)
• powershell://… (or a custom “evilproto://” that a malicious installer put on the box)
• data:text/html,
• javascript:alert(1)
• file://C:/windows/system32/calc.exe
• C:\somewhere\malicious.exe
• mybad.lnk or anything else with a registered verb

…ShellExecute will hand it off to whatever handler is registered. That means remote HTAs, LNKs that point to EXEs, custom protocols, PowerShell‐URI handlers, the Works.

If all you truly want to do is open an HTTP(S) address (and nothing else), you must allowlist the scheme

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if this is just a sample, we shouldn't be insecure. People are likely to copy these samples into real products.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this is really applicable here - this is the client configuration. If you're writing a MCP client, you can set the redirect URI to anything you want when you spin up the listener. Happy to discuss if you think otherwise.

// - Serves the PRM document at /.well-known/oauth-protected-resource
// - Checks Authorization header on requests
// - Returns 401 + WWW-Authenticate when authorization is missing or invalid
app.UseMcpAuthorization();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR introduces custom implementations for authorization, which reimplements much of the functionality already provided by ASP.NET Core's built-in authorization middleware. Leveraging the standard MapX().RequireAuthorization() approach would not only simplify the implementation for library consumers but also align with best practices.

Could you re-evaluate this implementation and consider using MapMcp().RequireAuthorization() instead of custom solutions for handling authorization? This would ensure better maintainability, security, and alignment with typical ASP.NET Core practices.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing so would allow for usage with all the builtin integrations and libraries that already support asp.net core authentication.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put a bit differently, if you want everyone inside and outside of msft to use this library, the code needs to meet them where they already are rather than forcing something new that has similar type names and concepts.

}

// Handle the PRM document endpoint if not handled by the endpoint
if (context.Request.Path.StartsWithSegments("/.well-known/oauth-protected-resource") &&

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is the metadata endpoint implemented as a middleware instead of as an app.Map call inside MapMcp?

/// <param name="context">The HTTP context.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task HandleAsync(HttpContext context)
{ _logger.LogDebug("Serving Protected Resource Metadata document");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log messages in the hot path need to be using LoggerMessage delegates to reduce allocations.

@localden
Copy link
Collaborator Author

localden commented May 2, 2025

@halter73 @DavidParks8 closing, as this is superseded by #377.

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.

3 participants