5#include <nlohmann/json.hpp>
26 std::optional<std::string> public_key_use;
27 StringList key_operations;
28 std::optional<std::string> algorithm;
29 std::optional<std::string> key_id;
30 std::optional<std::string> curve;
31 std::optional<std::string> x;
32 std::optional<std::string> y;
33 std::optional<std::string> modulus;
34 std::optional<std::string> exponent;
35 StringList certificate_chain;
41 std::vector<JsonWebKey> keys;
47 std::optional<std::string> key_id;
48 std::optional<std::string> algorithm;
49 std::optional<std::string> key_type;
50 std::optional<std::string> public_key_use;
74 const std::string& jwks_uri) = 0;
84 const std::string& jwks_uri)
override {
85 for (
const auto& entry : entries_) {
90 return std::optional<JsonWebKeySet>{};
95 for (
auto& entry : entries_) {
97 entry.second = std::move(keys);
101 entries_.emplace_back(std::move(jwks_uri), std::move(keys));
106 for (
auto iter = entries_.begin(); iter != entries_.end(); ++iter) {
108 entries_.erase(iter);
116 std::vector<std::pair<std::string, JsonWebKeySet>> entries_;
119namespace jwks_detail {
121using Json = nlohmann::json;
123inline std::optional<std::string> optional_string(
const Json& value,
125 const auto iter = value.find(key);
126 if (iter == value.end() || iter->is_null()) {
129 if (iter->is_string()) {
130 return iter->get<std::string>();
135inline std::string string_or_empty(
const Json& value,
const char* key) {
136 auto result = optional_string(value, key);
137 return result.has_value() ? *result : std::string{};
140inline StringList string_list_or_empty(
const Json& value,
const char* key) {
142 const auto iter = value.find(key);
143 if (iter == value.end() || !iter->is_array()) {
146 for (
const auto& entry : *iter) {
147 if (entry.is_string()) {
148 result.push_back(entry.get<std::string>());
154inline MetadataMap extension_metadata(
const Json& value) {
156 for (
auto iter = value.begin(); iter != value.end(); ++iter) {
157 const auto key = iter.key();
158 if (key ==
"keys" || key ==
"kty" || key ==
"use" || key ==
"key_ops" ||
159 key ==
"alg" || key ==
"kid" || key ==
"crv" || key ==
"x" ||
160 key ==
"y" || key ==
"n" || key ==
"e" || key ==
"x5c") {
163 if (iter->is_string()) {
164 result.emplace(key, iter->get<std::string>());
165 }
else if (iter->is_boolean()) {
166 result.emplace(key, iter->get<
bool>() ?
"true" :
"false");
167 }
else if (iter->is_number() || iter->is_object() || iter->is_array()) {
168 result.emplace(key, iter->dump());
174inline bool optional_matches(
const std::optional<std::string>& expected,
175 const std::optional<std::string>& actual) {
176 return !expected.has_value() || (actual.has_value() && *actual == *expected);
183 const nlohmann::json& value) {
184 if (!value.is_object()) {
185 return mcp::core::unexpected(
187 "JWK JSON value must be an object"));
191 key.key_type = jwks_detail::string_or_empty(value,
"kty");
192 if (key.key_type.empty()) {
194 OAuthErrorCode::kMetadataDiscoveryFailed,
"JWK kty is required"));
196 key.public_key_use = jwks_detail::optional_string(value,
"use");
197 key.key_operations = jwks_detail::string_list_or_empty(value,
"key_ops");
198 key.algorithm = jwks_detail::optional_string(value,
"alg");
199 key.key_id = jwks_detail::optional_string(value,
"kid");
200 key.curve = jwks_detail::optional_string(value,
"crv");
201 key.x = jwks_detail::optional_string(value,
"x");
202 key.y = jwks_detail::optional_string(value,
"y");
203 key.modulus = jwks_detail::optional_string(value,
"n");
204 key.exponent = jwks_detail::optional_string(value,
"e");
205 key.certificate_chain = jwks_detail::string_list_or_empty(value,
"x5c");
206 key.metadata = jwks_detail::extension_metadata(value);
212 const nlohmann::json& value) {
213 if (!value.is_object()) {
214 return mcp::core::unexpected(
216 "JWKS JSON value must be an object"));
218 const auto keys = value.find(
"keys");
219 if (keys == value.end() || !keys->is_array()) {
220 return mcp::core::unexpected(
222 "JWKS keys array is required"));
226 for (
const auto& entry : *keys) {
228 if (!parsed.has_value()) {
229 return mcp::core::unexpected(parsed.error());
231 set.keys.push_back(std::move(*parsed));
233 set.metadata = jwks_detail::extension_metadata(value);
243 std::optional<JsonWebKey> selected;
244 for (
const auto& key : set.keys) {
245 if (!jwks_detail::optional_matches(criteria.key_id, key.key_id) ||
246 !jwks_detail::optional_matches(criteria.algorithm, key.algorithm) ||
247 !jwks_detail::optional_matches(
248 criteria.key_type, std::optional<std::string>{key.key_type}) ||
249 !jwks_detail::optional_matches(criteria.public_key_use,
250 key.public_key_use)) {
253 if (selected.has_value()) {
254 return mcp::core::unexpected(
256 "JWKS key selection is ambiguous"));
260 if (!selected.has_value()) {
261 return mcp::core::unexpected(
263 "JWKS key selection found no matching key"));
In-process JWKS cache for tests and embedded clients.
Definition jwks.hpp:81
Application-provided JWKS cache boundary.
Definition jwks.hpp:69
Application-provided JWKS retrieval boundary.
Definition jwks.hpp:60
bool constant_time_string_equal(std::string_view lhs, std::string_view rhs) noexcept
Compare two strings without data-dependent early exit.
Definition constant_time.hpp:17
core::Result< JsonWebKeySet > parse_json_web_key_set(const nlohmann::json &value)
Parse a public JWKS document from JSON.
Definition jwks.hpp:211
core::Result< JsonWebKey > parse_json_web_key(const nlohmann::json &value)
Parse a public JWK from JSON without performing trust decisions.
Definition jwks.hpp:182
core::Result< JsonWebKey > select_json_web_key(const JsonWebKeySet &set, const JwkSelectionCriteria &criteria)
Select a single JWK by stable JWT header criteria.
Definition jwks.hpp:241
OAuth authorization lifecycle contracts and lightweight state logic.
core::Error make_oauth_error(OAuthErrorCode code, std::string message, std::string detail={})
Build an auth-category lifecycle error.
Definition lifecycle.hpp:50
Shared result and error primitives used by the public cxxmcp SDK.
std::monostate Unit
Success value for operations that only need to report failure.
Definition result.hpp:55
tl::expected< T, Error > Result
Alias for the SDK result type.
Definition result.hpp:64
JSON Web Key Set value model.
Definition jwks.hpp:40
Public JSON Web Key value model.
Definition jwks.hpp:24
Criteria for selecting a public JWK before verification.
Definition jwks.hpp:46
Request for retrieving a JWKS document.
Definition jwks.hpp:54