Skip to content

fix(auth): normalize explicit default ports in resource_url_from_server_url (RFC 3986) - #3310

Open
teddiesloco wants to merge 2 commits into
modelcontextprotocol:mainfrom
teddiesloco:fix/oauth-resource-url-default-port
Open

fix(auth): normalize explicit default ports in resource_url_from_server_url (RFC 3986)#3310
teddiesloco wants to merge 2 commits into
modelcontextprotocol:mainfrom
teddiesloco:fix/oauth-resource-url-default-port

Conversation

@teddiesloco

Copy link
Copy Markdown

Summary

Fixes #3297.

resource_url_from_server_url() lowercases the scheme/authority and strips URL fragments, but previously preserved explicitly specified default ports (:80 for HTTP, :443 for HTTPS).

Per RFC 3986 §6.2.3 (Scheme-Based Normalization), an explicit default port is equivalent to an omitted port. Furthermore, Pydantic's HttpUrl automatically normalizes metadata URLs by dropping default ports. Because resource_url_from_server_url() preserved them, check_resource_allowed() would reject an otherwise identical resource (e.g. comparing https://example.com:443/mcp against https://example.com/mcp), leading to false-positive authorization failures.

Changes

  1. _canonical_netloc() Helper: Normalizes netloc by stripping default port 80 for http and 443 for https, while preserving non-default ports, userinfo, and IPv6 literals.
  2. check_resource_allowed(): Canonicalizes both requested_resource and configured_resource before parsing to guarantee consistent comparison across equivalent URL representations.
  3. Tests:
    • Added unit tests verifying default ports 80 and 443 are stripped from canonical resource URLs while non-default ports like 8443 or 8080 are preserved.
    • Added tests verifying check_resource_allowed() correctly evaluates matching resources regardless of explicit default port notation in either parameter.

Verification

  • Ran uv run pytest tests/shared/test_auth_utils.py tests/client/test_auth.py: 155 passed, 1 xfailed (pre-existing).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/mcp/shared/auth_utils.py Outdated
- _canonical_netloc now catches ValueError from parsed.port for
  malformed explicit ports (e.g. out-of-range), falling back to the
  original netloc instead of letting check_resource_allowed() crash
- add test coverage for the userinfo-in-netloc and IPv6-literal
  branches that were previously untested (CI requires 100% coverage)
- add regression test for the malformed-port fallback
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.

resource_url_from_server_url preserves explicit default ports

1 participant