cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
registration.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
5#include <optional>
6#include <string>
7#include <string_view>
8#include <utility>
9
11#include "cxxmcp/auth/types.hpp"
13
16
17namespace mcp::auth {
18
21 StringList redirect_uris;
22 StringList grant_types;
23 StringList response_types;
24 ScopeList scope;
25 std::optional<std::string> client_name;
26 std::optional<std::string> client_uri;
27 std::optional<std::string> logo_uri;
28 std::optional<std::string> contacts;
29 std::optional<std::string> token_endpoint_auth_method;
30 MetadataMap metadata;
31};
32
35 std::string client_id;
36 std::optional<std::string> client_secret;
37 std::optional<std::string> registration_access_token;
38 std::optional<std::string> registration_client_uri;
39 std::optional<std::string> client_id_issued_at;
40 std::optional<std::string> client_secret_expires_at;
41 ClientRegistrationRequest client_metadata;
42 MetadataMap metadata;
43};
44
47 std::string client_id;
48 ClientRegistrationRequest client_metadata;
49 MetadataMap metadata;
50};
51
54 std::string registration_endpoint;
55 HeaderMap headers;
56 ClientRegistrationRequest registration;
57};
58
61 std::string client_id_metadata_url;
62 HeaderMap headers;
63};
64
70 public:
71 virtual ~OAuthClientRegistrationEndpoint() = default;
72
73 virtual core::Result<ClientRegistrationResponse> register_client(
74 const ClientRegistrationEndpointRequest& request) = 0;
75};
76
82 public:
83 virtual ~ClientIdMetadataDocumentEndpoint() = default;
84
85 virtual core::Result<ClientIdMetadataDocument> fetch_client_metadata_document(
86 const ClientIdMetadataDocumentFetchRequest& request) = 0;
87};
88
91 std::string client_name = "MCP Client";
92 std::string redirect_uri;
93 ScopeList scopes;
94 MetadataMap metadata;
95};
96
99 std::optional<std::string> client_id_metadata_url;
100 std::string client_name = "MCP Client";
101 std::string redirect_uri;
102 ScopeList scopes;
103 HeaderMap headers;
104 MetadataMap metadata;
105};
106
110 if (options.redirect_uri.empty()) {
111 return mcp::core::unexpected(
112 core::Error{1,
113 "redirect_uri is required for dynamic client registration",
114 {},
115 std::string(AuthErrorCategory)});
116 }
117
119 request.redirect_uris = {options.redirect_uri};
120 request.grant_types = {"authorization_code", "refresh_token"};
121 request.response_types = {"code"};
122 request.scope = options.scopes;
123 request.client_name =
124 options.client_name.empty() ? "MCP Client" : options.client_name;
125 request.token_endpoint_auth_method = "none";
126 request.metadata = options.metadata;
127 return request;
128}
129
132 const ClientRegistrationResponse& response, std::string redirect_uri,
133 ScopeList scopes) {
134 OAuthClientConfig config;
135 config.client_id = response.client_id;
136 if (response.client_secret.has_value() && !response.client_secret->empty()) {
137 config.client_secret = response.client_secret;
138 }
139 config.redirect_uri = std::move(redirect_uri);
140 config.scopes = std::move(scopes);
141 config.metadata = response.metadata;
142 return config;
143}
144
147 const ClientIdMetadataDocument& document, std::string redirect_uri,
148 ScopeList scopes) {
149 OAuthClientConfig config;
150 config.client_id = document.client_id;
151 config.redirect_uri = std::move(redirect_uri);
152 config.scopes = std::move(scopes);
153 config.metadata = document.metadata;
154 return config;
155}
156
157namespace detail {
158
159inline bool metadata_flag_enabled(const MetadataMap& metadata,
160 std::string_view key) {
161 const auto iter = metadata.find(std::string(key));
162 if (iter == metadata.end()) {
163 return false;
164 }
165 return iter->second == "true" || iter->second == "1" || iter->second == "yes";
166}
167
168inline bool segment_is_dot_reference(std::string_view segment) {
169 std::string normalized;
170 normalized.reserve(segment.size());
171 for (std::size_t index = 0; index < segment.size(); ++index) {
172 if (segment[index] == '%' && index + 2 < segment.size() &&
173 segment[index + 1] == '2' &&
174 (segment[index + 2] == 'e' || segment[index + 2] == 'E')) {
175 normalized.push_back('.');
176 index += 2;
177 continue;
178 }
179 normalized.push_back(segment[index]);
180 }
181 return normalized == "." || normalized == "..";
182}
183
184inline bool url_contains_dot_segment(std::string_view path) {
185 std::size_t pos = 0;
186 while (pos <= path.size()) {
187 const auto next = path.find('/', pos);
188 const auto end = next == std::string_view::npos ? path.size() : next;
189 const auto segment = path.substr(pos, end - pos);
190 if (segment_is_dot_reference(segment)) {
191 return true;
192 }
193 if (next == std::string_view::npos) {
194 break;
195 }
196 pos = next + 1;
197 }
198 return false;
199}
200
201} // namespace detail
202
205 const AuthorizationServerMetadata& metadata) {
206 return detail::metadata_flag_enabled(metadata.metadata,
207 "client_id_metadata_document_supported");
208}
209
211inline bool is_valid_client_id_metadata_url(std::string_view url) {
212 constexpr std::string_view kHttps = "https://";
213 if (url.substr(0, kHttps.size()) != kHttps) {
214 return false;
215 }
216 const auto authority_start = kHttps.size();
217 const auto path_start = url.find('/', authority_start);
218 if (path_start == std::string_view::npos || path_start + 1 >= url.size()) {
219 return false;
220 }
221 const auto authority =
222 url.substr(authority_start, path_start - authority_start);
223 if (authority.empty() || authority.find('@') != std::string_view::npos) {
224 return false;
225 }
226 const auto path = url.substr(path_start);
227 if (path.find('#') != std::string_view::npos ||
228 detail::url_contains_dot_segment(path)) {
229 return false;
230 }
231 return true;
232}
233
234} // namespace mcp::auth
Shared lightweight value types for cxxmcp auth contracts.
URL-based Client ID Metadata Document network boundary.
Definition registration.hpp:81
Dynamic client registration network boundary.
Definition registration.hpp:69
RFC 9728 and RFC 8414 metadata value models.
core::Result< ClientRegistrationRequest > build_client_registration_request(const ClientRegistrationOptions &options)
Build the default MCP OAuth DCR request for a public client.
Definition registration.hpp:109
bool supports_client_id_metadata_document(const AuthorizationServerMetadata &metadata)
Whether authorization metadata advertises URL-based client_id docs.
Definition registration.hpp:204
bool is_valid_client_id_metadata_url(std::string_view url)
Validate an SEP-991-style HTTPS URL client_id.
Definition registration.hpp:211
OAuthClientConfig oauth_client_config_from_registration_response(const ClientRegistrationResponse &response, std::string redirect_uri, ScopeList scopes)
Convert a successful DCR response into the SDK OAuth client config.
Definition registration.hpp:131
OAuthClientConfig oauth_client_config_from_metadata_document(const ClientIdMetadataDocument &document, std::string redirect_uri, ScopeList scopes)
Convert a Client ID Metadata Document into the SDK OAuth config.
Definition registration.hpp:146
Shared result and error primitives used by the public cxxmcp SDK.
tl::expected< T, Error > Result
Alias for the SDK result type.
Definition result.hpp:64
RFC 8414 OAuth authorization server metadata.
Definition metadata.hpp:28
Options used when selecting a client_id before authorization.
Definition registration.hpp:98
Request sent through an application-provided CIMD fetch transport.
Definition registration.hpp:60
Client ID Metadata Document shape used by MCP OAuth clients.
Definition registration.hpp:46
Request sent through an application-provided DCR transport.
Definition registration.hpp:53
Options used to build a dynamic client registration request.
Definition registration.hpp:90
RFC 7591 dynamic client registration request.
Definition registration.hpp:20
Dynamic client registration response.
Definition registration.hpp:34
Public OAuth client configuration used by lifecycle helpers.
Definition types.hpp:41
Structured error returned by fallible SDK operations.
Definition result.hpp:35