mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 07:09:11 +02:00
fix: forward resource_base_url and issuer_url through KeycloakOAuthProxy
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f19df2dce7
commit
2e1178fa84
2 changed files with 46 additions and 0 deletions
|
|
@ -124,6 +124,8 @@ class KeycloakOAuthProxy(OAuthProxy):
|
|||
upstream_client_id: str,
|
||||
upstream_client_secret: str | None = None,
|
||||
base_url: AnyHttpUrl | str,
|
||||
resource_base_url: AnyHttpUrl | str | None = None,
|
||||
issuer_url: AnyHttpUrl | str | None = None,
|
||||
required_scopes: list[str] | str | None = None,
|
||||
valid_scopes: list[str] | str | None = None,
|
||||
audience: str | list[str] | None = None,
|
||||
|
|
@ -149,6 +151,13 @@ class KeycloakOAuthProxy(OAuthProxy):
|
|||
upstream_client_id: Client ID of the application registered in Keycloak.
|
||||
upstream_client_secret: Client secret. Optional for public clients.
|
||||
base_url: Public URL of this FastMCP server.
|
||||
resource_base_url: Optional public base URL for the protected resource
|
||||
metadata and token audience. Defaults to `base_url`. Set this when
|
||||
the resource is served under a different origin/path than the
|
||||
proxy (e.g. behind a gateway).
|
||||
issuer_url: Issuer URL for OAuth metadata. Defaults to `base_url`. Set
|
||||
this when the authorization-server metadata must advertise a
|
||||
different issuer than `base_url` (e.g. behind a gateway).
|
||||
required_scopes: Scopes to require on incoming tokens. Defaults to `["openid"]`.
|
||||
valid_scopes: Scopes advertised to clients via the `/.well-known` endpoints
|
||||
and accepted during Dynamic Client Registration. Defaults to
|
||||
|
|
@ -224,6 +233,8 @@ class KeycloakOAuthProxy(OAuthProxy):
|
|||
valid_scopes=parsed_valid_scopes,
|
||||
redirect_path=redirect_path,
|
||||
base_url=base_url,
|
||||
resource_base_url=resource_base_url,
|
||||
issuer_url=issuer_url,
|
||||
jwt_signing_key=jwt_signing_key,
|
||||
client_storage=client_storage,
|
||||
require_authorization_consent=require_authorization_consent,
|
||||
|
|
|
|||
|
|
@ -995,6 +995,41 @@ class TestKeycloakOAuthProxy:
|
|||
)
|
||||
assert proxy._redirect_path == "/custom/oauth/callback"
|
||||
|
||||
def test_issuer_and_resource_base_url_default_to_base_url(self, jwt_verifier):
|
||||
proxy = KeycloakOAuthProxy(
|
||||
realm_url="https://keycloak.example.com/realms/test",
|
||||
upstream_client_id="test-client",
|
||||
upstream_client_secret="test-secret",
|
||||
token_verifier=jwt_verifier,
|
||||
base_url="https://proxy.example.com",
|
||||
jwt_signing_key="test-secret-key",
|
||||
client_storage=MemoryStore(),
|
||||
)
|
||||
assert str(proxy.issuer_url).rstrip("/") == "https://proxy.example.com"
|
||||
assert proxy.resource_base_url is None
|
||||
|
||||
def test_issuer_and_resource_base_url_forwarded(self, jwt_verifier):
|
||||
"""Behind a gateway, switching to KeycloakOAuthProxy must not drop the
|
||||
issuer/resource overrides that OAuthProxy supports (correct metadata
|
||||
and JWT audience)."""
|
||||
proxy = KeycloakOAuthProxy(
|
||||
realm_url="https://keycloak.example.com/realms/test",
|
||||
upstream_client_id="test-client",
|
||||
upstream_client_secret="test-secret",
|
||||
token_verifier=jwt_verifier,
|
||||
base_url="https://internal.proxy.local",
|
||||
issuer_url="https://public.gateway.example.com",
|
||||
resource_base_url="https://public.gateway.example.com/mcp",
|
||||
jwt_signing_key="test-secret-key",
|
||||
client_storage=MemoryStore(),
|
||||
)
|
||||
assert str(proxy.issuer_url).rstrip("/") == "https://public.gateway.example.com"
|
||||
assert proxy.resource_base_url is not None
|
||||
assert (
|
||||
str(proxy.resource_base_url).rstrip("/")
|
||||
== "https://public.gateway.example.com/mcp"
|
||||
)
|
||||
|
||||
async def test_refresh_expires_in_zero_subsequent_refresh_does_not_shrink(
|
||||
self, proxy
|
||||
):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue