fix(mcp): reuse cached oauth redirect port on re-auth - #49249
fix(mcp): reuse cached oauth redirect port on re-auth#49249x9x9x9x9x9x91 wants to merge 1 commit into
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for tracing both OAuth provider-construction paths. The current-main premise is valid: tools/mcp_oauth.py:830 and tools/mcp_oauth_manager.py:544 each allocate a fresh port for redirect_port: 0.
Problems
tools/mcp_oauth.py:165acceptslocalhost, but_build_client_metadata()always reconstructshttp://127.0.0.1:<port>/callback(tools/mcp_oauth.py:849). A cachedlocalhostregistration therefore still gets a different redirect URI. Preserve the cached authority or limit this reuse path to127.0.0.1, with a regression test.- The new fallback helper should also make malformed cache shapes and invalid URI ports fall through to fresh-port allocation; its
data.get(...)andparsed.portaccesses are outside the guarded parse operation. Add focused negative tests.
Suggested changes
- Add localhost and malformed-cache coverage alongside the existing cached-127.0.0.1 test.
Automated hermes-sweeper review.
| continue | ||
| if ( | ||
| parsed.scheme == "http" | ||
| and parsed.hostname in {"127.0.0.1", "localhost"} |
There was a problem hiding this comment.
This accepts a cached localhost URI, but the later metadata builder always emits http://127.0.0.1:<port>/callback. That still changes the registered URI on re-auth. Please preserve the cached authority or restrict this branch to canonical 127.0.0.1, and add a localhost regression test.
| if not data: | ||
| return None | ||
|
|
||
| for uri in data.get("redirect_uris") or []: |
There was a problem hiding this comment.
Please validate the decoded client-info shape and keep URI port extraction inside the malformed-data fallback path. The helper promises None for unusable registrations, but these accesses can raise instead of reaching _find_free_port().
|
Merged on main via PR #65718 with your commit cherry-picked and authorship preserved — thanks @x9x9x9x9x9x91! The salvage composed your cached-port precedence with two changes that landed after your branch: the reserved-socket TOCTOU pool (#65622) now only engages for truly-fresh ephemeral picks, and |
Problem
On MCP OAuth re-authentication, the callback server binds a new random free port each time (
_find_free_port()), while reusing the stored dynamic-client-registrationclient_id. Many providers pin the redirect URI(s) to the values registered at first auth and reject a callback on a different port (invalid_redirect_uri/ no matching registered URI), so re-auth against a cached client registration can fail even though theclient_idis still valid.Fix
When a cached client registration exists, reuse the redirect port parsed from the stored registration instead of picking a fresh random one:
_cached_redirect_port(storage)— returns the loopback callback port from the cached client registration's redirect URI, orNoneif absent/unparseable.redirect_urithen share the previously-registered port, so the callback matches a registered URI on re-auth.Test
Extends
tests/tools/test_mcp_oauth.pyto cover the cached-port reuse path and the no-cache fallback. 69 passed.Note: this touches the same
mcp_oauth.pyarea as the recent OAuth hardening (#2552); rebased clean on currentmain.