fix(desktop): omit a null iss from the oauth.callback relay; hoist the loopback parse import

The Electron listener now always emits iss (null when the server sent none)
and McpOauthCallbackParams is extra="forbid", so a new Desktop against a
backend without this change would fail every remote MCP OAuth login with a
4000 - including providers that never send iss. Send the key only when set.
Also drop the deliver_callback_flow test the RPC test subsumes.
This commit is contained in:
kshitijk4poor
2026-09-15 12:42:39 +05:30
committed by kshitij
parent 4a164a8073
commit 8bdac1b17a
3 changed files with 5 additions and 15 deletions

View File

@@ -135,7 +135,9 @@ export async function completeMcpDesktopOAuth({
throw new Error(callback.error || 'OAuth callback did not include state')
}
await request('callback', { session_id: flowId, ...callback })
// Omit a null iss: a backend that predates the RFC 9207 relay rejects unknown params (4000).
const { iss, ...rest } = callback
await request('callback', { session_id: flowId, ...rest, ...(iss ? { iss } : {}) })
})
.catch(error => {
relayError = error

View File

@@ -189,18 +189,6 @@ def test_deliver_callback_accepts_matching_state():
assert flow._callback == ("abc", "s3cr3tstate", None)
def test_deliver_callback_forwards_iss():
"""The client-redirect relay carries RFC 9207 ``iss`` into the flow. Desktop drives this path
against a remote backend, and mcp 2.x rejects a response missing ``iss`` when the authorization
server advertised ``authorization_response_iss_parameter_supported``."""
flow = _make_session()
out = deliver_callback_flow(
"sess-relay-1", "hosp", code="abc", state="s3cr3tstate", iss="https://as.example.com"
)
assert out["ok"] is True
assert flow._callback == ("abc", "s3cr3tstate", "https://as.example.com")
def test_oauth_callback_rpc_relays_iss():
"""The gateway ``mcp.servers.oauth.callback`` RPC accepts ``iss`` under the extra=forbid contract
and forwards it to the flow; the desktop renderer always sends the key (possibly null)."""

View File

@@ -49,6 +49,8 @@ def _validate_client_redirect_uri(uri: str) -> str:
def _start_loopback_listener(flow) -> "http.server.HTTPServer":
"""Bind a loopback callback listener feeding ``flow.deliver_callback``; returns the
HTTPServer already serving on a daemon thread (caller pins ``flow.redirect_uri`` from it)."""
from tools.mcp_oauth import _parse_redirect_query
class _Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self): # noqa: N802 — stdlib naming
parsed = urlparse(self.path)
@@ -56,8 +58,6 @@ def _start_loopback_listener(flow) -> "http.server.HTTPServer":
self.send_response(404)
self.end_headers()
return
from tools.mcp_oauth import _parse_redirect_query
body = b"<h1>Authorization received</h1><p>You can close this tab and return to Hermes.</p>"
status = 200
try: