40 auto response = fetch_json(request);
41 if (!response.has_value()) {
42 return mcp::core::unexpected(response.error());
44 return parse_protected_resource_metadata(*response);
49 auto response = fetch_json(request);
50 if (!response.has_value()) {
51 return mcp::core::unexpected(response.error());
53 return parse_authorization_server_metadata(*response);
57 using Json = nlohmann::json;
62 OAuthErrorCode::kMetadataDiscoveryFailed,
63 "OAuth metadata HTTP GET endpoint is not configured"));
66 auto response = get_(request);
67 if (!response.has_value()) {
68 return mcp::core::unexpected(response.error());
70 if (response->status_code < 200 || response->status_code >= 300) {
72 OAuthErrorCode::kMetadataDiscoveryFailed,
73 "OAuth metadata endpoint returned non-success HTTP status",
74 std::to_string(response->status_code)));
78 return Json::parse(response->body);
79 }
catch (
const Json::exception& error) {
81 OAuthErrorCode::kMetadataDiscoveryFailed,
82 "OAuth metadata endpoint returned invalid JSON", error.what()));
86 static std::optional<std::string> optional_string(
const Json& value,
88 const auto iter = value.find(key);
89 if (iter == value.end() || iter->is_null()) {
92 if (iter->is_string()) {
93 return iter->get<std::string>();
98 static std::string string_or_empty(
const Json& value,
const char* key) {
99 auto result = optional_string(value, key);
100 return result.has_value() ? *result : std::string{};
103 static StringList string_list_or_empty(
const Json& value,
const char* key) {
105 const auto iter = value.find(key);
106 if (iter == value.end() || !iter->is_array()) {
109 for (
const auto& entry : *iter) {
110 if (entry.is_string()) {
111 result.push_back(entry.get<std::string>());
117 static MetadataMap extension_metadata(
const Json& value) {
119 for (
auto iter = value.begin(); iter != value.end(); ++iter) {
120 if (iter->is_string()) {
121 result.emplace(iter.key(), iter->get<std::string>());
122 }
else if (iter->is_boolean()) {
123 result.emplace(iter.key(), iter->get<
bool>() ?
"true" :
"false");
124 }
else if (iter->is_number() || iter->is_object() || iter->is_array()) {
125 result.emplace(iter.key(), iter->dump());
132 parse_protected_resource_metadata(
const Json& value) {
133 if (!value.is_object()) {
135 OAuthErrorCode::kMetadataDiscoveryFailed,
136 "protected resource metadata JSON must be an object"));
140 metadata.resource = string_or_empty(value,
"resource");
141 metadata.authorization_servers =
142 string_list_or_empty(value,
"authorization_servers");
143 metadata.bearer_methods_supported =
144 string_list_or_empty(value,
"bearer_methods_supported");
145 metadata.resource_signing_alg_values_supported =
146 string_list_or_empty(value,
"resource_signing_alg_values_supported");
147 metadata.scopes_supported = string_list_or_empty(value,
"scopes_supported");
148 metadata.resource_name = optional_string(value,
"resource_name");
149 metadata.resource_documentation =
150 optional_string(value,
"resource_documentation");
151 metadata.metadata = extension_metadata(value);
156 parse_authorization_server_metadata(
const Json& value) {
157 if (!value.is_object()) {
159 OAuthErrorCode::kMetadataDiscoveryFailed,
160 "authorization server metadata JSON must be an object"));
164 metadata.issuer = string_or_empty(value,
"issuer");
165 metadata.authorization_endpoint =
166 string_or_empty(value,
"authorization_endpoint");
167 metadata.token_endpoint = string_or_empty(value,
"token_endpoint");
168 metadata.registration_endpoint =
169 optional_string(value,
"registration_endpoint");
170 metadata.jwks_uri = optional_string(value,
"jwks_uri");
171 metadata.response_types_supported =
172 string_list_or_empty(value,
"response_types_supported");
173 metadata.grant_types_supported =
174 string_list_or_empty(value,
"grant_types_supported");
175 metadata.code_challenge_methods_supported =
176 string_list_or_empty(value,
"code_challenge_methods_supported");
177 metadata.token_endpoint_auth_methods_supported =
178 string_list_or_empty(value,
"token_endpoint_auth_methods_supported");
179 metadata.dpop_signing_alg_values_supported =
180 string_list_or_empty(value,
"dpop_signing_alg_values_supported");
181 metadata.scopes_supported = string_list_or_empty(value,
"scopes_supported");
182 metadata.metadata = extension_metadata(value);