mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-22 13:34:17 +02:00
Add resource_server_url parameter to OAuth proxy providers (#1682)
This commit is contained in:
parent
183275c8c3
commit
8c678b552b
11 changed files with 69 additions and 49 deletions
|
|
@ -159,7 +159,7 @@ The `OAuthProxy` class provides the complete proxy implementation:
|
|||
</ParamField>
|
||||
|
||||
<ParamField body="resource_server_url" type="AnyHttpUrl | str | None">
|
||||
Resource server URL (defaults to base_url)
|
||||
Path of the FastMCP server (defaults to base_url). **Important**: This should point to your MCP endpoint path. For example, if your MCP server is accessible at `{base_url}/mcp`, specify `https://your-server.com/mcp` here for proper RFC 8707 compliance.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="allowed_client_redirect_uris" type="list[str] | None">
|
||||
|
|
@ -228,7 +228,10 @@ auth = OAuthProxy(
|
|||
base_url="https://your-server.com",
|
||||
|
||||
# Optional: customize callback path (defaults to "/auth/callback")
|
||||
redirect_path="/auth/callback"
|
||||
redirect_path="/auth/callback",
|
||||
|
||||
# Optional: specify MCP endpoint path if different from base_url
|
||||
# resource_server_url="https://your-server.com/mcp"
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="My Server", auth=auth)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ token_verifier = JWTVerifier(
|
|||
auth = RemoteAuthProvider(
|
||||
token_verifier=token_verifier,
|
||||
authorization_servers=[AnyHttpUrl("https://auth.yourcompany.com")],
|
||||
resource_server_url="https://api.yourcompany.com",
|
||||
resource_server_url="https://api.yourcompany.com/mcp", # Point to your MCP endpoint
|
||||
# Optional: customize allowed client redirect URIs (defaults to localhost only)
|
||||
allowed_client_redirect_uris=["http://localhost:*", "http://127.0.0.1:*"]
|
||||
)
|
||||
|
|
@ -121,7 +121,7 @@ mcp = FastMCP(name="Company API", auth=auth)
|
|||
|
||||
This configuration creates a server that accepts tokens issued by `auth.yourcompany.com` and provides the OAuth discovery metadata that MCP clients need. The `JWTVerifier` handles token validation using your identity provider's public keys, while the `RemoteAuthProvider` generates the required OAuth endpoints.
|
||||
|
||||
The `authorization_servers` list tells MCP clients which identity providers you trust. The `resource_server_url` identifies your server in OAuth metadata, enabling proper token audience validation.
|
||||
The `authorization_servers` list tells MCP clients which identity providers you trust. The `resource_server_url` identifies your server in OAuth metadata, enabling proper token audience validation. **Important**: The `resource_server_url` should point to your actual MCP endpoint - for example, if your MCP server is accessible at `https://api.yourcompany.com/mcp`, use that full path rather than just the base URL.
|
||||
|
||||
### Custom Endpoints
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ class CompanyAuthProvider(RemoteAuthProvider):
|
|||
super().__init__(
|
||||
token_verifier=token_verifier,
|
||||
authorization_servers=[AnyHttpUrl("https://auth.yourcompany.com")],
|
||||
resource_server_url="https://api.yourcompany.com"
|
||||
resource_server_url="https://api.yourcompany.com/mcp" # Your MCP endpoint path
|
||||
)
|
||||
|
||||
def get_routes(self) -> list[Route]:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ auth = AzureProvider(
|
|||
tenant_id=os.getenv("AZURE_TENANT_ID")
|
||||
or "", # Required for single-tenant apps - get from Azure Portal
|
||||
base_url="http://localhost:8000",
|
||||
resource_server_url="http://localhost:8000/mcp",
|
||||
# redirect_path="/auth/callback", # Default path - change if using a different callback URL
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ auth = GitHubProvider(
|
|||
client_id=os.getenv("FASTMCP_SERVER_AUTH_GITHUB_CLIENT_ID") or "",
|
||||
client_secret=os.getenv("FASTMCP_SERVER_AUTH_GITHUB_CLIENT_SECRET") or "",
|
||||
base_url="http://localhost:8000",
|
||||
resource_server_url="http://localhost:8000/mcp",
|
||||
# redirect_path="/auth/callback", # Default path - change if using a different callback URL
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ auth = GoogleProvider(
|
|||
client_id=os.getenv("FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_ID") or "",
|
||||
client_secret=os.getenv("FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_SECRET") or "",
|
||||
base_url="http://localhost:8000",
|
||||
resource_server_url="http://localhost:8000/mcp",
|
||||
# redirect_path="/auth/callback", # Default path - change if using a different callback URL
|
||||
# Optional: specify required scopes
|
||||
# required_scopes=["openid", "https://www.googleapis.com/auth/userinfo.email"],
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ auth = WorkOSProvider(
|
|||
client_secret=os.getenv("WORKOS_CLIENT_SECRET") or "",
|
||||
authkit_domain=os.getenv("WORKOS_AUTHKIT_DOMAIN") or "https://your-app.authkit.app",
|
||||
base_url="http://localhost:8000",
|
||||
resource_server_url="http://localhost:8000/mcp",
|
||||
# redirect_path="/auth/callback", # Default path - change if using a different callback URL
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -254,45 +254,20 @@ class OAuthProxy(OAuthProvider):
|
|||
upstream_client_secret: Client secret for upstream server
|
||||
upstream_revocation_endpoint: Optional upstream revocation endpoint
|
||||
token_verifier: Token verifier for validating access tokens
|
||||
base_url: Public URL of this FastMCP server
|
||||
base_url: Public URL of the server that exposes this FastMCP server; redirect path is
|
||||
relative to this URL
|
||||
redirect_path: Redirect path configured in upstream OAuth app (defaults to "/auth/callback")
|
||||
issuer_url: Issuer URL for OAuth metadata (defaults to base_url)
|
||||
service_documentation_url: Optional service documentation URL
|
||||
resource_server_url: Resource server URL (defaults to base_url)
|
||||
resource_server_url: Path of the FastMCP server. If None, FastMCP will
|
||||
attempt to overwrite this with the correct path to the server
|
||||
e.g. {base_url}/mcp
|
||||
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
|
||||
Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*").
|
||||
If None (default), only localhost redirect URIs are allowed.
|
||||
If empty list, all redirect URIs are allowed (not recommended for production).
|
||||
These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app.
|
||||
"""
|
||||
# Convert string URLs to AnyHttpUrl for parent class
|
||||
base_url_parsed = (
|
||||
AnyHttpUrl(base_url) if isinstance(base_url, str) else base_url
|
||||
)
|
||||
issuer_url_parsed = (
|
||||
(AnyHttpUrl(issuer_url) if isinstance(issuer_url, str) else issuer_url)
|
||||
if issuer_url
|
||||
else None
|
||||
)
|
||||
service_documentation_url_parsed = (
|
||||
(
|
||||
AnyHttpUrl(service_documentation_url)
|
||||
if isinstance(service_documentation_url, str)
|
||||
else service_documentation_url
|
||||
)
|
||||
if service_documentation_url
|
||||
else None
|
||||
)
|
||||
resource_server_url_parsed = (
|
||||
(
|
||||
AnyHttpUrl(resource_server_url)
|
||||
if isinstance(resource_server_url, str)
|
||||
else resource_server_url
|
||||
)
|
||||
if resource_server_url
|
||||
else None
|
||||
)
|
||||
|
||||
# Always enable DCR since we implement it locally for MCP clients
|
||||
client_registration_options = ClientRegistrationOptions(enabled=True)
|
||||
|
||||
|
|
@ -302,13 +277,13 @@ class OAuthProxy(OAuthProvider):
|
|||
)
|
||||
|
||||
super().__init__(
|
||||
base_url=base_url_parsed,
|
||||
issuer_url=issuer_url_parsed,
|
||||
service_documentation_url=service_documentation_url_parsed,
|
||||
base_url=base_url,
|
||||
issuer_url=issuer_url,
|
||||
service_documentation_url=service_documentation_url,
|
||||
client_registration_options=client_registration_options,
|
||||
revocation_options=revocation_options,
|
||||
required_scopes=token_verifier.required_scopes,
|
||||
resource_server_url=resource_server_url_parsed,
|
||||
resource_server_url=resource_server_url,
|
||||
)
|
||||
|
||||
# Store upstream configuration
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ class AzureProviderSettings(BaseSettings):
|
|||
redirect_path: str | None = None
|
||||
required_scopes: list[str] | None = None
|
||||
timeout_seconds: int | None = None
|
||||
resource_server_url: str | None = None
|
||||
allowed_client_redirect_uris: list[str] | None = None
|
||||
|
||||
@field_validator("required_scopes", mode="before")
|
||||
@classmethod
|
||||
|
|
@ -160,7 +162,8 @@ class AzureProvider(OAuthProxy):
|
|||
redirect_path: str | NotSetT = NotSet,
|
||||
required_scopes: list[str] | None | NotSetT = NotSet,
|
||||
timeout_seconds: int | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | None = None,
|
||||
resource_server_url: str | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
|
||||
):
|
||||
"""Initialize Azure OAuth provider.
|
||||
|
||||
|
|
@ -172,6 +175,8 @@ class AzureProvider(OAuthProxy):
|
|||
redirect_path: Redirect path configured in Azure (defaults to "/auth/callback")
|
||||
required_scopes: Required scopes (defaults to ["User.Read", "email", "openid", "profile"])
|
||||
timeout_seconds: HTTP request timeout for Azure API calls
|
||||
resource_server_url: Path of the FastMCP server (defaults to base_url). If your MCP endpoint is at
|
||||
a different path like {base_url}/mcp, specify it here for RFC 8707 compliance.
|
||||
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
|
||||
If None (default), all URIs are allowed. If empty list, no URIs are allowed.
|
||||
"""
|
||||
|
|
@ -186,6 +191,8 @@ class AzureProvider(OAuthProxy):
|
|||
"redirect_path": redirect_path,
|
||||
"required_scopes": required_scopes,
|
||||
"timeout_seconds": timeout_seconds,
|
||||
"resource_server_url": resource_server_url,
|
||||
"allowed_client_redirect_uris": allowed_client_redirect_uris,
|
||||
}.items()
|
||||
if v is not NotSet
|
||||
}
|
||||
|
|
@ -220,6 +227,8 @@ class AzureProvider(OAuthProxy):
|
|||
"openid",
|
||||
"profile",
|
||||
]
|
||||
resource_server_url_final = settings.resource_server_url or base_url_final
|
||||
allowed_client_redirect_uris_final = settings.allowed_client_redirect_uris
|
||||
|
||||
# Extract secret string from SecretStr
|
||||
client_secret_str = (
|
||||
|
|
@ -250,7 +259,8 @@ class AzureProvider(OAuthProxy):
|
|||
base_url=base_url_final,
|
||||
redirect_path=redirect_path_final,
|
||||
issuer_url=base_url_final,
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris,
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris_final,
|
||||
resource_server_url=resource_server_url_final,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ class GitHubProviderSettings(BaseSettings):
|
|||
redirect_path: str | None = None
|
||||
required_scopes: list[str] | None = None
|
||||
timeout_seconds: int | None = None
|
||||
resource_server_url: AnyHttpUrl | str | None = None
|
||||
allowed_client_redirect_uris: list[str] | None = None
|
||||
|
||||
@field_validator("required_scopes", mode="before")
|
||||
@classmethod
|
||||
|
|
@ -199,9 +201,10 @@ class GitHubProvider(OAuthProxy):
|
|||
client_secret: str | NotSetT = NotSet,
|
||||
base_url: AnyHttpUrl | str | NotSetT = NotSet,
|
||||
redirect_path: str | NotSetT = NotSet,
|
||||
required_scopes: list[str] | None | NotSetT = NotSet,
|
||||
required_scopes: list[str] | NotSetT = NotSet,
|
||||
timeout_seconds: int | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | None = None,
|
||||
resource_server_url: AnyHttpUrl | str | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
|
||||
):
|
||||
"""Initialize GitHub OAuth provider.
|
||||
|
||||
|
|
@ -212,6 +215,8 @@ class GitHubProvider(OAuthProxy):
|
|||
redirect_path: Redirect path configured in GitHub OAuth app (defaults to "/auth/callback")
|
||||
required_scopes: Required GitHub scopes (defaults to ["user"])
|
||||
timeout_seconds: HTTP request timeout for GitHub API calls
|
||||
resource_server_url: Path of the FastMCP server (defaults to base_url). If your MCP endpoint is at
|
||||
a different path like {base_url}/mcp, specify it here for RFC 8707 compliance.
|
||||
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
|
||||
If None (default), all URIs are allowed. If empty list, no URIs are allowed.
|
||||
"""
|
||||
|
|
@ -225,6 +230,8 @@ class GitHubProvider(OAuthProxy):
|
|||
"redirect_path": redirect_path,
|
||||
"required_scopes": required_scopes,
|
||||
"timeout_seconds": timeout_seconds,
|
||||
"resource_server_url": resource_server_url,
|
||||
"allowed_client_redirect_uris": allowed_client_redirect_uris,
|
||||
}.items()
|
||||
if v is not NotSet
|
||||
}
|
||||
|
|
@ -245,6 +252,8 @@ class GitHubProvider(OAuthProxy):
|
|||
redirect_path_final = settings.redirect_path or "/auth/callback"
|
||||
timeout_seconds_final = settings.timeout_seconds or 10
|
||||
required_scopes_final = settings.required_scopes or ["user"]
|
||||
resource_server_url_final = settings.resource_server_url or base_url_final
|
||||
allowed_client_redirect_uris_final = settings.allowed_client_redirect_uris
|
||||
|
||||
# Create GitHub token verifier
|
||||
token_verifier = GitHubTokenVerifier(
|
||||
|
|
@ -267,7 +276,8 @@ class GitHubProvider(OAuthProxy):
|
|||
base_url=base_url_final,
|
||||
redirect_path=redirect_path_final,
|
||||
issuer_url=base_url_final, # We act as the issuer for client registration
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris,
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris_final,
|
||||
resource_server_url=resource_server_url_final,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ class GoogleProviderSettings(BaseSettings):
|
|||
redirect_path: str | None = None
|
||||
required_scopes: list[str] | None = None
|
||||
timeout_seconds: int | None = None
|
||||
resource_server_url: AnyHttpUrl | str | None = None
|
||||
allowed_client_redirect_uris: list[str] | None = None
|
||||
|
||||
@field_validator("required_scopes", mode="before")
|
||||
@classmethod
|
||||
|
|
@ -215,9 +217,10 @@ class GoogleProvider(OAuthProxy):
|
|||
client_secret: str | NotSetT = NotSet,
|
||||
base_url: AnyHttpUrl | str | NotSetT = NotSet,
|
||||
redirect_path: str | NotSetT = NotSet,
|
||||
required_scopes: list[str] | None | NotSetT = NotSet,
|
||||
required_scopes: list[str] | NotSetT = NotSet,
|
||||
timeout_seconds: int | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | None = None,
|
||||
resource_server_url: AnyHttpUrl | str | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
|
||||
):
|
||||
"""Initialize Google OAuth provider.
|
||||
|
||||
|
|
@ -244,6 +247,8 @@ class GoogleProvider(OAuthProxy):
|
|||
"redirect_path": redirect_path,
|
||||
"required_scopes": required_scopes,
|
||||
"timeout_seconds": timeout_seconds,
|
||||
"resource_server_url": resource_server_url,
|
||||
"allowed_client_redirect_uris": allowed_client_redirect_uris,
|
||||
}.items()
|
||||
if v is not NotSet
|
||||
}
|
||||
|
|
@ -265,6 +270,8 @@ class GoogleProvider(OAuthProxy):
|
|||
timeout_seconds_final = settings.timeout_seconds or 10
|
||||
# Google requires at least one scope - openid is the minimal OIDC scope
|
||||
required_scopes_final = settings.required_scopes or ["openid"]
|
||||
resource_server_url_final = settings.resource_server_url or base_url_final
|
||||
allowed_client_redirect_uris_final = settings.allowed_client_redirect_uris
|
||||
|
||||
# Create Google token verifier
|
||||
token_verifier = GoogleTokenVerifier(
|
||||
|
|
@ -287,7 +294,8 @@ class GoogleProvider(OAuthProxy):
|
|||
base_url=base_url_final,
|
||||
redirect_path=redirect_path_final,
|
||||
issuer_url=base_url_final, # We act as the issuer for client registration
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris,
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris_final,
|
||||
resource_server_url=resource_server_url_final,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ class WorkOSProviderSettings(BaseSettings):
|
|||
redirect_path: str | None = None
|
||||
required_scopes: list[str] | None = None
|
||||
timeout_seconds: int | None = None
|
||||
resource_server_url: AnyHttpUrl | str | None = None
|
||||
allowed_client_redirect_uris: list[str] | None = None
|
||||
|
||||
@field_validator("required_scopes", mode="before")
|
||||
@classmethod
|
||||
|
|
@ -167,7 +169,8 @@ class WorkOSProvider(OAuthProxy):
|
|||
redirect_path: str | NotSetT = NotSet,
|
||||
required_scopes: list[str] | None | NotSetT = NotSet,
|
||||
timeout_seconds: int | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | None = None,
|
||||
resource_server_url: AnyHttpUrl | str | NotSetT = NotSet,
|
||||
allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
|
||||
):
|
||||
"""Initialize WorkOS OAuth provider.
|
||||
|
||||
|
|
@ -179,6 +182,8 @@ class WorkOSProvider(OAuthProxy):
|
|||
redirect_path: Redirect path configured in WorkOS (defaults to "/auth/callback")
|
||||
required_scopes: Required OAuth scopes (no default)
|
||||
timeout_seconds: HTTP request timeout for WorkOS API calls
|
||||
resource_server_url: Path of the FastMCP server (defaults to base_url). If your MCP endpoint is at
|
||||
a different path like {base_url}/mcp, specify it here for RFC 8707 compliance.
|
||||
allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
|
||||
If None (default), all URIs are allowed. If empty list, no URIs are allowed.
|
||||
"""
|
||||
|
|
@ -193,6 +198,8 @@ class WorkOSProvider(OAuthProxy):
|
|||
"redirect_path": redirect_path,
|
||||
"required_scopes": required_scopes,
|
||||
"timeout_seconds": timeout_seconds,
|
||||
"resource_server_url": resource_server_url,
|
||||
"allowed_client_redirect_uris": allowed_client_redirect_uris,
|
||||
}.items()
|
||||
if v is not NotSet
|
||||
}
|
||||
|
|
@ -221,6 +228,8 @@ class WorkOSProvider(OAuthProxy):
|
|||
redirect_path_final = settings.redirect_path or "/auth/callback"
|
||||
timeout_seconds_final = settings.timeout_seconds or 10
|
||||
scopes_final = settings.required_scopes or []
|
||||
resource_server_url_final = settings.resource_server_url or base_url_final
|
||||
allowed_client_redirect_uris_final = settings.allowed_client_redirect_uris
|
||||
|
||||
# Extract secret string from SecretStr
|
||||
client_secret_str = (
|
||||
|
|
@ -244,7 +253,8 @@ class WorkOSProvider(OAuthProxy):
|
|||
base_url=base_url_final,
|
||||
redirect_path=redirect_path_final,
|
||||
issuer_url=base_url_final,
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris,
|
||||
allowed_client_redirect_uris=allowed_client_redirect_uris_final,
|
||||
resource_server_url=resource_server_url_final,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue