Skip to content

Conversation

@maxisbey
Copy link
Contributor

Summary

Pydantic's AnyHttpUrl automatically appends a trailing slash to bare hostnames (e.g., http://localhost:8000 becomes http://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 returned issuer/resource URL 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_serializer to 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 AnyHttpUrl representation is unchanged, but the JSON responses no longer include spurious trailing slashes.

Fixes #1919
Fixes #1265

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.
@claude
Copy link

claude bot commented Jan 23, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Comment on lines +132 to +136
@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("/")
Copy link
Member

Choose a reason for hiding this comment

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

Can we just use str instead of AnyHttpUrl in the field?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants