Skip to content

Add support for remote-oauth-support Fix #686 #764

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ravibits
Copy link

@ravibits ravibits commented May 20, 2025

This contains fix for the issue: #686

As per the new authorization spec for MCP Servers as outlined here: https://modelcontextprotocol.io/specification/draft/basic/authorization, implementing the remote oauth support for the FastMCP Servers.

  • Add support for /.well-known/oauth-protected-resource endpoint when the MCP Server is created with support with auth based off of remote authorization server
  • Add support for custom bearer token validation with default JWT based bearer token validation out of the box for MCP servers with remote oauth support.
  • Implement middleware to check for the JWT token validity
  • (optional) at a per tool level, create indicators for whether auth required and if yes, what scopes are required for that tool.

As part of the spec, the only responsibility of the MCP Server should be to indicate to the client it's oauth protected resource and indicate to the client where to find the authorization server.

This is just the initial version and based on the feedback from @localden, @ihrpr , I intend to keep making changes to complete the test cases and full feature documentation.

Motivation and Context

Implement supoprt for RFC9728, along with other requirements outlined in the spec.

How Has This Been Tested?

  • TODO: We will implement a simple MCP Server using an authorization server that supports PKCE, DCR etc and ensure that the MCP Server is receiving the access tokens as presented by the MCP client.

Breaking Changes

May have breaking changes.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@ihrpr ihrpr added this to the auth-spec milestone May 23, 2025
@ochafik ochafik self-requested a review July 8, 2025 14:11
Copy link

@ochafik ochafik left a comment

Choose a reason for hiding this comment

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

Hey @ravibits, thanks a lot for sending this out, it's an exciting set of changes!

Suggested a few fixes & tweaks but my main concern is about updating our copy of FastMCP 1.0 (as opposed to updating / upgrading to FastMCP 2.0).

requires-python = ">=3.10"
authors = [{ name = "Anthropic, PBC." }]
license = { text = "MIT" }
dependencies = [
Copy link

Choose a reason for hiding this comment

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

Needs additional pyjwt dep here



class TokenValidatorJWT(TokenValidator[AccessToken]):
def __init__(self, resource_metadata: ProtectedResourceMetadata):
Copy link

Choose a reason for hiding this comment

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

Probably needs super init:

        super().__init__()

# Set environment variables first (see above)

# Run the server
uv run mcp-simple-auth
Copy link

Choose a reason for hiding this comment

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

uv run mcp_simple_remote_auth

uv run mcp-simple-auth
```

The server will start on `http://localhost:8000`.
Copy link

Choose a reason for hiding this comment

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

I'd make it more explicit that it starts on http://localhost:8000/sse


This server supports multiple transport protocols that can run on the same port:

#### SSE (Server-Sent Events) - Default
Copy link

Choose a reason for hiding this comment

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

Can we switch the default to streamable http / move sse down? SSE is now deprecated

Also, seems that connecting to the SSE server doesn't trigger authentication the way it does to the Streamable HTTP server.

3. No other service is using port 8000
4. The transport specified is valid (`sse` or `streamable-http`)

You can use [Inspector](https://github.com/modelcontextprotocol/inspector) to test Auth
Copy link

Choose a reason for hiding this comment

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

Have you found an Inspector workflow that works for you? Even after applying the couple of fixes I suggested, I hit a wall w/ the AS not setting CORS headers that would allow localhost to proceed.

@@ -138,14 +140,23 @@ def __init__(
self,
name: str | None = None,
instructions: str | None = None,
auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any]
| None = None,
auth_server_details: dict[str, Any] | None = None,
Copy link

Choose a reason for hiding this comment

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

While these changes make sense, an important consideration is this class comes from FastMCP 1.0.

I reckon we'll probably want to update the SDK to FastMCP 2.0 at some point, so might be better not to diverge our version of FastMCP, if possible.

I'd suggest maybe to evaluate if the same change is needed / applicable to FastMCP 2.0 and send a PR to them, while we figure out if / how we can update to 2.0? (if they accept the patch, we can even backport it to our 1.0 fork if the overall upgrade to 2.0 isn't straightforward, without fearing that said upgrade would be harder down the line).

Does this make sense?

status_code=404, detail="Protected resource metadata not configured"
)
return Response(
self._protected_resource_metadata.model_dump_json(),
Copy link

Choose a reason for hiding this comment

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

            self._protected_resource_metadata.model_dump_json(exclude_none=True),

(otherwise fields that are optional but not-nullable, like jwks_uri, fail to be validated if set to None, e.g. by the TS SDK (used by the Inspector)

routes.append(
Route(
"/.well-known/oauth-protected-resource",
self._serve_protected_resource_metadata,
Copy link

Choose a reason for hiding this comment

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

We might need to wrap this one with cors_middleware as done elsewhere in the sdk

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