Repository navigation
Conversation
Pydantic's AnyHttpUrl automatically appends a trailing slash to bare hostnames (e.g., http://localhost:8000 becomes http://localhost:8000/). This causes OAuth discovery to fail in clients that validate per RFC 8414 §3.3 and RFC 9728 §3, which require the returned issuer/resource URL to be identical to the URL used for discovery. Add field_serializer to OAuthMetadata.issuer, ProtectedResourceMetadata.resource, and ProtectedResourceMetadata.authorization_servers to strip the trailing slash during JSON serialization. Fixes #1919 Fixes #1265 Reported-by: joar Github-Issue: #1919
@classmethod is not the intended decorator for Pydantic's field_serializer (unlike field_validator which requires it). Using @staticmethod avoids IDE warnings about incorrect descriptor protocol usage.
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
| @field_serializer("issuer") | ||
| @staticmethod | ||
| def _serialize_issuer(v: AnyHttpUrl) -> str: | ||
| """Strip trailing slash added by AnyHttpUrl for RFC 8414 §3.3 compliance.""" | ||
| return str(v).rstrip("/") |
There was a problem hiding this comment.
Can we just use str instead of AnyHttpUrl in the field?
|
Real-world breakage report in support of this PR — the strict issuer comparison is blocking several production MCP OAuth logins today. Reproduction (SnapTrade MCP): the server advertises its authorization server as It's a cluster, not one server. Hermes Agent (which uses the
…and a sibling variant where the emitted Normalization on the serializer side (this PR's approach, RFC 8414 §3.3 / RFC 9728 §3) would fix the advertised-vs-issuer mismatch across the board, and it matches the local patches we're running. Happy to test a release candidate if that helps. Thanks! |
Summary
Pydantic's
AnyHttpUrlautomatically appends a trailing slash to bare hostnames (e.g.,http://localhost:8000becomeshttp://localhost:8000/). This causes OAuth metadata discovery to fail in clients that validate per RFC 8414 §3.3 and RFC 9728 §3, which require the returnedissuer/resourceURL to be identical to the URL used for discovery.This broke interop with Google ADK and IBM's MCP Context Forge, which correctly perform this identity check.
Changes
Add
field_serializerto strip trailing slashes during JSON serialization for:OAuthMetadata.issuer(RFC 8414 §3.3)ProtectedResourceMetadata.resource(RFC 9728 §3)ProtectedResourceMetadata.authorization_servers(RFC 9728 §3)The fix is at the serialization layer so the internal
AnyHttpUrlrepresentation is unchanged, but the JSON responses no longer include spurious trailing slashes.Fixes #1919
Fixes #1265