RFC 7591 OAuth 2.0 Dynamic Client Registration Metadata. See https://datatracker.ietf.org/doc/html/rfc7591#section-2
| 45 | |
| 46 | |
| 47 | class OAuthClientMetadata(BaseModel): |
| 48 | """RFC 7591 OAuth 2.0 Dynamic Client Registration Metadata. |
| 49 | See https://datatracker.ietf.org/doc/html/rfc7591#section-2 |
| 50 | """ |
| 51 | |
| 52 | model_config = ConfigDict(url_preserve_empty_path=True) |
| 53 | |
| 54 | redirect_uris: list[AnyUrl] | None = Field(..., min_length=1) |
| 55 | # supported auth methods for the token endpoint |
| 56 | token_endpoint_auth_method: ( |
| 57 | Literal["none", "client_secret_post", "client_secret_basic", "private_key_jwt"] | None |
| 58 | ) = None |
| 59 | # supported grant_types of this implementation |
| 60 | grant_types: list[ |
| 61 | Literal["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:jwt-bearer"] | str |
| 62 | ] = [ |
| 63 | "authorization_code", |
| 64 | "refresh_token", |
| 65 | ] |
| 66 | # The MCP spec requires the "code" response type, but OAuth |
| 67 | # servers may also return additional types they support |
| 68 | response_types: list[str] = ["code"] |
| 69 | scope: str | None = None |
| 70 | # SEP-837: OIDC application_type. Defaults to "native" since MCP clients typically use |
| 71 | # loopback redirect URIs; set "web" for remote browser-based clients on a non-local host. |
| 72 | application_type: Literal["web", "native"] = "native" |
| 73 | |
| 74 | # these fields are currently unused, but we support & store them for potential |
| 75 | # future use |
| 76 | client_name: str | None = None |
| 77 | client_uri: AnyHttpUrl | None = None |
| 78 | logo_uri: AnyHttpUrl | None = None |
| 79 | contacts: list[str] | None = None |
| 80 | tos_uri: AnyHttpUrl | None = None |
| 81 | policy_uri: AnyHttpUrl | None = None |
| 82 | jwks_uri: AnyHttpUrl | None = None |
| 83 | jwks: Any | None = None |
| 84 | software_id: str | None = None |
| 85 | software_version: str | None = None |
| 86 | |
| 87 | @field_validator( |
| 88 | "client_uri", |
| 89 | "logo_uri", |
| 90 | "tos_uri", |
| 91 | "policy_uri", |
| 92 | "jwks_uri", |
| 93 | mode="before", |
| 94 | ) |
| 95 | @classmethod |
| 96 | def _empty_string_optional_url_to_none(cls, v: object) -> object: |
| 97 | # RFC 7591 §2 marks these URL fields OPTIONAL. Some authorization servers |
| 98 | # echo omitted metadata back as "" instead of dropping the keys, which |
| 99 | # AnyHttpUrl would otherwise reject — throwing away an otherwise valid |
| 100 | # registration response. Treat "" as absent. |
| 101 | if v == "": |
| 102 | return None |
| 103 | return v |
| 104 |
no outgoing calls