Skip to main content

rust_mcp_sdk/auth/
metadata.rs

1use std::borrow::Cow;
2
3use crate::{
4    auth::{AuthorizationServerMetadata, OauthProtectedResourceMetadata},
5    error::McpSdkError,
6    utils::join_url,
7};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use thiserror::Error;
11use url::Url;
12
13pub const WELL_KNOWN_OAUTH_AUTHORIZATION_SERVER: &str = "/.well-known/oauth-authorization-server";
14pub const OAUTH_PROTECTED_RESOURCE_BASE: &str = "/.well-known/oauth-protected-resource";
15
16#[allow(unused)]
17#[derive(Hash, Eq, PartialEq, Clone)]
18pub enum OauthEndpoint {
19    AuthorizationEndpoint,
20    TokenEndpoint,
21    RegistrationEndpoint,
22    RevocationEndpoint,
23    IntrospectionEndpoint,
24    AuthorizationServerMetadata,
25    ProtectedResourceMetadata,
26}
27
28#[derive(Debug, Error)]
29pub enum AuthMetadateError {
30    #[error("Url Parse Error: {0}")]
31    Transport(#[from] url::ParseError),
32}
33
34pub struct AuthMetadataEndpoints {
35    pub protected_resource_endpoint: String,
36    pub authorization_server_endpoint: String,
37}
38
39// Builder struct to construct both OAuthMetadata and OAuthProtectedResourceMetadata
40
41#[derive(Default)]
42pub struct AuthMetadataBuilder<'a> {
43    // OAuthMetadata-specific fields
44    issuer: Option<Cow<'a, str>>,
45    authorization_endpoint: Option<Cow<'a, str>>,
46    token_endpoint: Option<Cow<'a, str>>,
47    registration_endpoint: Option<Cow<'a, str>>,
48    revocation_endpoint: Option<Cow<'a, str>>,
49    introspection_endpoint: Option<Cow<'a, str>>,
50    scopes_supported: Option<Vec<Cow<'a, str>>>,
51
52    response_types_supported: Option<Vec<Cow<'a, str>>>,
53    response_modes_supported: Option<Vec<Cow<'a, str>>>,
54    grant_types_supported: Option<Vec<Cow<'a, str>>>,
55    token_endpoint_auth_methods_supported: Option<Vec<Cow<'a, str>>>,
56    token_endpoint_auth_signing_alg_values_supported: Option<Vec<Cow<'a, str>>>,
57    revocation_endpoint_auth_signing_alg_values_supported: Option<Vec<Cow<'a, str>>>,
58    revocation_endpoint_auth_methods_supported: Option<Vec<Cow<'a, str>>>,
59    introspection_endpoint_auth_methods_supported: Option<Vec<Cow<'a, str>>>,
60    introspection_endpoint_auth_signing_alg_values_supported: Option<Vec<Cow<'a, str>>>,
61    code_challenge_methods_supported: Option<Vec<Cow<'a, str>>>,
62    service_documentation: Option<Cow<'a, str>>,
63
64    // OAuthProtectedResourceMetadata-specific fields
65    resource: Option<Cow<'a, str>>,
66    authorization_servers: Option<Vec<Cow<'a, str>>>,
67    required_scopes: Option<Vec<Cow<'a, str>>>,
68
69    jwks_uri: Option<Cow<'a, str>>,
70    bearer_methods_supported: Option<Vec<Cow<'a, str>>>,
71    resource_signing_alg_values_supported: Option<Vec<Cow<'a, str>>>,
72    resource_name: Option<Cow<'a, str>>,
73    resource_documentation: Option<Cow<'a, str>>,
74    resource_policy_uri: Option<Cow<'a, str>>,
75    resource_tos_uri: Option<Cow<'a, str>>,
76    tls_client_certificate_bound_access_tokens: Option<bool>,
77    authorization_details_types_supported: Option<Vec<Cow<'a, str>>>,
78    dpop_signing_alg_values_supported: Option<Vec<Cow<'a, str>>>,
79    dpop_bound_access_tokens_required: Option<bool>,
80
81    // none-standard
82    userinfo_endpoint: Option<Cow<'a, str>>,
83}
84
85// Result struct to hold both metadata types
86#[derive(Debug, Serialize, Deserialize, Clone)]
87pub struct OauthMetadata {
88    authorization_server_metadata: AuthorizationServerMetadata,
89    protected_resource_metadata: OauthProtectedResourceMetadata,
90}
91
92impl OauthMetadata {
93    pub fn protected_resource_metadata(&self) -> &OauthProtectedResourceMetadata {
94        &self.protected_resource_metadata
95    }
96
97    pub fn authorization_server_metadata(&self) -> &AuthorizationServerMetadata {
98        &self.authorization_server_metadata
99    }
100
101    pub fn endpoints(&self) -> AuthMetadataEndpoints {
102        AuthMetadataEndpoints {
103            authorization_server_endpoint: WELL_KNOWN_OAUTH_AUTHORIZATION_SERVER.to_string(),
104            protected_resource_endpoint: format!(
105                "{OAUTH_PROTECTED_RESOURCE_BASE}{}",
106                match self.protected_resource_metadata.resource.path() {
107                    "/" => "",
108                    other => other,
109                }
110            ),
111        }
112    }
113}
114
115impl<'a> AuthMetadataBuilder<'a> {
116    fn with_defaults(protected_resource: &'a str) -> Self {
117        Self {
118            response_types_supported: Some(vec!["code".into()]),
119            code_challenge_methods_supported: Some(vec!["S256".into()]),
120            token_endpoint_auth_methods_supported: Some(vec!["client_secret_post".into()]),
121            grant_types_supported: Some(vec!["authorization_code".into(), "refresh_token".into()]),
122            resource: Some(protected_resource.into()),
123            ..Default::default()
124        }
125    }
126
127    /// Creates a new instance of the builder for the given protected resource.
128    /// The `protected_resource` parameter must specify the full URL of the MCP server.
129    pub fn new(protected_resource_url: &'a str) -> Self {
130        Self::with_defaults(protected_resource_url)
131    }
132
133    pub async fn from_discovery_url<S>(
134        discovery_url: &str,
135        protected_resource: S,
136        required_scopes: Vec<S>,
137    ) -> Result<Self, McpSdkError>
138    where
139        S: Into<Cow<'a, str>>,
140    {
141        let client = crate::auth::shared_http_client();
142        let json: Value = client
143            .get(discovery_url)
144            .send()
145            .await
146            .map_err(|e| McpSdkError::Internal {
147                description: format!(
148                    "Failed to fetch discovery document : \"{discovery_url}\": {e}"
149                ),
150            })?
151            .error_for_status()
152            .map_err(|e| McpSdkError::Internal {
153                description: format!("Discovery endpoint returned error: {e}"),
154            })?
155            .json()
156            .await
157            .map_err(|e| McpSdkError::Internal {
158                description: format!("Failed to parse JSON from discovery document: {e}"),
159            })?;
160
161        // Helper to extract string field safely
162        let get_str = |key: &str| {
163            json.get(key)
164                .and_then(|v| v.as_str())
165                .map(|s| Cow::<str>::Owned(s.to_string()))
166        };
167        // Helper for optional array of strings
168        let get_str_array = |key: &str| {
169            json.get(key).and_then(|v| v.as_array()).map(|arr| {
170                arr.iter()
171                    .filter_map(|item| item.as_str())
172                    .filter(|v| !v.is_empty())
173                    .map(|s| Cow::<str>::Owned(s.to_string()))
174                    .collect::<Vec<_>>()
175            })
176        };
177
178        let issuer = get_str("issuer").ok_or_else(|| McpSdkError::Internal {
179            description: "Missing 'issuer' in discovery document".to_string(),
180        })?;
181
182        Ok(Self {
183            issuer: Some(issuer.clone()),
184            authorization_endpoint: get_str("authorization_endpoint"),
185            scopes_supported: get_str_array("scopes_supported"),
186            required_scopes: Some(required_scopes.into_iter().map(|s| s.into()).collect()),
187            token_endpoint: get_str("token_endpoint"),
188            jwks_uri: get_str("jwks_uri"),
189
190            userinfo_endpoint: get_str("userinfo_endpoint"),
191
192            registration_endpoint: get_str("registration_endpoint"),
193            revocation_endpoint: get_str("revocation_endpoint"),
194            introspection_endpoint: get_str("introspection_endpoint"),
195            response_types_supported: get_str_array("response_types_supported"),
196            response_modes_supported: get_str_array("response_modes_supported"),
197            grant_types_supported: get_str_array("grant_types_supported"),
198            token_endpoint_auth_methods_supported: get_str_array(
199                "token_endpoint_auth_methods_supported",
200            ),
201            token_endpoint_auth_signing_alg_values_supported: get_str_array(
202                "token_endpoint_auth_signing_alg_values_supported",
203            ),
204            revocation_endpoint_auth_signing_alg_values_supported: get_str_array(
205                "revocation_endpoint_auth_signing_alg_values_supported",
206            ),
207            revocation_endpoint_auth_methods_supported: get_str_array(
208                "revocation_endpoint_auth_methods_supported",
209            ),
210            introspection_endpoint_auth_methods_supported: get_str_array(
211                "introspection_endpoint_auth_methods_supported",
212            ),
213            introspection_endpoint_auth_signing_alg_values_supported: get_str_array(
214                "introspection_endpoint_auth_signing_alg_values_supported",
215            ),
216            code_challenge_methods_supported: get_str_array("code_challenge_methods_supported"),
217            service_documentation: get_str("service_documentation"),
218            resource: Some(protected_resource.into()),
219            authorization_servers: Some(vec![issuer]),
220            bearer_methods_supported: None,
221            resource_signing_alg_values_supported: None,
222            resource_name: None,
223            resource_documentation: None,
224            resource_policy_uri: None,
225            resource_tos_uri: None,
226            tls_client_certificate_bound_access_tokens: None,
227            authorization_details_types_supported: None,
228            dpop_signing_alg_values_supported: None,
229            dpop_bound_access_tokens_required: None,
230        })
231    }
232
233    fn parse_url_field<S>(
234        field_name: &str,
235        value: Option<S>,
236        base_url: Option<&Url>,
237    ) -> Result<Url, McpSdkError>
238    where
239        S: Into<Cow<'a, str>>,
240    {
241        let value = value
242            .ok_or(McpSdkError::Internal {
243                description: format!("Error: '{field_name}' is missing."),
244            })?
245            .into();
246
247        let url = if value.contains("://") {
248            // Absolute URL
249            Url::parse(&value)
250        } else if let Some(base_url) = base_url {
251            // Relative URL, join with base_url
252            join_url(base_url, &value)
253        } else {
254            // No base_url provided, try to parse as absolute URL anyway
255            Url::parse(&value)
256        };
257
258        url.map_err(|e| McpSdkError::Internal {
259            description: format!("Error: '{field_name}' is not a valid URL: {e}"),
260        })
261    }
262
263    fn parse_optional_url_field<S>(
264        field_name: &str,
265        value: Option<S>,
266        base_url: Option<&Url>,
267    ) -> Result<Option<Url>, McpSdkError>
268    where
269        S: Into<Cow<'a, str>>,
270    {
271        value
272            .map(|v| {
273                let value = v.into();
274                if value.contains("://") {
275                    // Absolute URL
276                    Url::parse(&value)
277                } else if let Some(base_url) = base_url {
278                    // Relative URL, join with base_url
279                    join_url(base_url, &value)
280                } else {
281                    // No base_url provided, try to parse as absolute URL anyway
282                    Url::parse(&value)
283                }
284            })
285            .transpose()
286            .map_err(|e| McpSdkError::Internal {
287                description: format!("Error: '{field_name}' is not a valid URL: {e}"),
288            })
289    }
290
291    pub fn scopes_supported<S>(mut self, scopes: Vec<S>) -> Self
292    where
293        S: Into<Cow<'a, str>>,
294    {
295        self.scopes_supported = Some(scopes.into_iter().map(|s| s.into()).collect());
296        self
297    }
298
299    // OAuthMetadata setters
300    pub fn issuer<S>(mut self, issuer: S) -> Self
301    where
302        S: Into<Cow<'a, str>>,
303    {
304        self.issuer = Some(issuer.into());
305        self
306    }
307
308    pub fn service_documentation<S>(mut self, url: S) -> Self
309    where
310        S: Into<Cow<'a, str>>,
311    {
312        self.service_documentation = Some(url.into());
313        self
314    }
315
316    pub fn authorization_endpoint<S>(mut self, url: S) -> Self
317    where
318        S: Into<Cow<'a, str>>,
319    {
320        self.authorization_endpoint = Some(url.into());
321        self
322    }
323
324    pub fn token_endpoint<S>(mut self, url: S) -> Self
325    where
326        S: Into<Cow<'a, str>>,
327    {
328        self.token_endpoint = Some(url.into());
329        self
330    }
331
332    pub fn response_types_supported<S>(mut self, types: Vec<S>) -> Self
333    where
334        S: Into<Cow<'a, str>>,
335    {
336        self.response_types_supported = Some(types.into_iter().map(|s| s.into()).collect());
337        self
338    }
339
340    pub fn response_modes_supported<S>(mut self, modes: Vec<S>) -> Self
341    where
342        S: Into<Cow<'a, str>>,
343    {
344        self.response_modes_supported = Some(modes.into_iter().map(|s| s.into()).collect());
345        self
346    }
347
348    pub fn registration_endpoint(mut self, url: &'a str) -> Self {
349        self.registration_endpoint = Some(url.into());
350        self
351    }
352
353    pub fn userinfo_endpoint(mut self, url: &'a str) -> Self {
354        self.userinfo_endpoint = Some(url.into());
355        self
356    }
357
358    pub fn grant_types_supported<S>(mut self, types: Vec<S>) -> Self
359    where
360        S: Into<Cow<'a, str>>,
361    {
362        self.grant_types_supported = Some(types.into_iter().map(|s| s.into()).collect());
363        self
364    }
365
366    pub fn token_endpoint_auth_methods_supported<S>(mut self, methods: Vec<S>) -> Self
367    where
368        S: Into<Cow<'a, str>>,
369    {
370        self.token_endpoint_auth_methods_supported =
371            Some(methods.into_iter().map(|s| s.into()).collect());
372        self
373    }
374
375    pub fn token_endpoint_auth_signing_alg_values_supported<S>(mut self, algs: Vec<S>) -> Self
376    where
377        S: Into<Cow<'a, str>>,
378    {
379        self.token_endpoint_auth_signing_alg_values_supported =
380            Some(algs.into_iter().map(|s| s.into()).collect());
381        self
382    }
383
384    pub fn revocation_endpoint(mut self, url: &'a str) -> Self {
385        self.revocation_endpoint = Some(url.into());
386        self
387    }
388
389    pub fn revocation_endpoint_auth_methods_supported<S>(mut self, methods: Vec<S>) -> Self
390    where
391        S: Into<Cow<'a, str>>,
392    {
393        self.revocation_endpoint_auth_methods_supported =
394            Some(methods.into_iter().map(|s| s.into()).collect());
395        self
396    }
397
398    pub fn revocation_endpoint_auth_signing_alg_values_supported<S>(mut self, algs: Vec<S>) -> Self
399    where
400        S: Into<Cow<'a, str>>,
401    {
402        self.revocation_endpoint_auth_signing_alg_values_supported =
403            Some(algs.into_iter().map(|s| s.into()).collect());
404        self
405    }
406
407    pub fn introspection_endpoint(mut self, endpoint: &'a str) -> Self {
408        self.introspection_endpoint = Some(endpoint.into());
409        self
410    }
411
412    pub fn introspection_endpoint_auth_methods_supported<S>(mut self, methods: Vec<S>) -> Self
413    where
414        S: Into<Cow<'a, str>>,
415    {
416        self.introspection_endpoint_auth_methods_supported =
417            Some(methods.into_iter().map(|s| s.into()).collect());
418        self
419    }
420
421    pub fn introspection_endpoint_auth_signing_alg_values_supported<S>(
422        mut self,
423        algs: Vec<String>,
424    ) -> Self
425    where
426        S: Into<Cow<'a, str>>,
427    {
428        self.introspection_endpoint_auth_signing_alg_values_supported =
429            Some(algs.into_iter().map(|s| s.into()).collect());
430        self
431    }
432
433    pub fn code_challenge_methods_supported<S>(mut self, methods: Vec<S>) -> Self
434    where
435        S: Into<Cow<'a, str>>,
436    {
437        self.code_challenge_methods_supported =
438            Some(methods.into_iter().map(|s| s.into()).collect());
439        self
440    }
441
442    // OAuthProtectedResourceMetadata setters
443    pub fn resource(mut self, url: &'a str) -> Self {
444        self.resource = Some(url.into());
445        self
446    }
447
448    pub fn authorization_servers(mut self, servers: Vec<&'a str>) -> Self {
449        self.authorization_servers = Some(servers.into_iter().map(|s| s.into()).collect());
450        self
451    }
452
453    pub fn reqquired_scopes<S>(mut self, scopes: Vec<S>) -> Self
454    where
455        S: Into<Cow<'a, str>>,
456    {
457        self.required_scopes = Some(scopes.into_iter().map(|s| s.into()).collect());
458        self
459    }
460
461    pub fn resource_documentation<S>(mut self, doc: String) -> Self
462    where
463        S: Into<Cow<'a, str>>,
464    {
465        self.resource_documentation = Some(doc.into());
466        self
467    }
468
469    pub fn jwks_uri(mut self, url: &'a str) -> Self {
470        self.jwks_uri = Some(url.into());
471        self
472    }
473
474    pub fn bearer_methods_supported<S>(mut self, methods: Vec<S>) -> Self
475    where
476        S: Into<Cow<'a, str>>,
477    {
478        self.bearer_methods_supported = Some(methods.into_iter().map(|s| s.into()).collect());
479        self
480    }
481
482    pub fn resource_signing_alg_values_supported<S>(mut self, algs: Vec<S>) -> Self
483    where
484        S: Into<Cow<'a, str>>,
485    {
486        self.resource_signing_alg_values_supported =
487            Some(algs.into_iter().map(|s| s.into()).collect());
488        self
489    }
490
491    pub fn resource_name<S>(mut self, name: S) -> Self
492    where
493        S: Into<Cow<'a, str>>,
494    {
495        self.resource_name = Some(name.into());
496        self
497    }
498
499    pub fn resource_policy_uri(mut self, url: &'a str) -> Self {
500        self.resource_policy_uri = Some(url.into());
501        self
502    }
503
504    pub fn resource_tos_uri(mut self, url: &'a str) -> Self {
505        self.resource_tos_uri = Some(url.into());
506        self
507    }
508
509    pub fn tls_client_certificate_bound_access_tokens(mut self, value: bool) -> Self {
510        self.tls_client_certificate_bound_access_tokens = Some(value);
511        self
512    }
513
514    pub fn authorization_details_types_supported<S>(mut self, types: Vec<S>) -> Self
515    where
516        S: Into<Cow<'a, str>>,
517    {
518        self.authorization_details_types_supported =
519            Some(types.into_iter().map(|s| s.into()).collect());
520        self
521    }
522
523    pub fn dpop_signing_alg_values_supported<S>(mut self, algs: Vec<S>) -> Self
524    where
525        S: Into<Cow<'a, str>>,
526    {
527        self.dpop_signing_alg_values_supported = Some(algs.into_iter().map(|s| s.into()).collect());
528        self
529    }
530
531    pub fn dpop_bound_access_tokens_required(mut self, value: bool) -> Self {
532        self.dpop_bound_access_tokens_required = Some(value);
533        self
534    }
535
536    // Build method to construct OauthMetadata
537    pub fn build(
538        self,
539    ) -> Result<(AuthorizationServerMetadata, OauthProtectedResourceMetadata), McpSdkError> {
540        let issuer = Self::parse_url_field("issuer", self.issuer, None)?;
541
542        let authorization_endpoint = Self::parse_url_field(
543            "authorization_endpoint",
544            self.authorization_endpoint,
545            Some(&issuer),
546        )?;
547
548        let token_endpoint =
549            Self::parse_url_field("token_endpoint", self.token_endpoint, Some(&issuer))?;
550
551        let registration_endpoint = Self::parse_optional_url_field(
552            "registration_endpoint",
553            self.registration_endpoint,
554            Some(&issuer),
555        )?;
556
557        let revocation_endpoint = Self::parse_optional_url_field(
558            "revocation_endpoint",
559            self.revocation_endpoint,
560            Some(&issuer),
561        )?;
562
563        let introspection_endpoint = Self::parse_optional_url_field(
564            "introspection_endpoint",
565            self.introspection_endpoint,
566            Some(&issuer),
567        )?;
568
569        let service_documentation = Self::parse_optional_url_field(
570            "service_documentation",
571            self.service_documentation,
572            None,
573        )?;
574
575        let jwks_uri = Self::parse_optional_url_field("jwks_uri", self.jwks_uri, Some(&issuer))?;
576
577        let authorization_server_metadata = AuthorizationServerMetadata {
578            issuer,
579            authorization_endpoint,
580            token_endpoint,
581            registration_endpoint,
582            service_documentation,
583            revocation_endpoint,
584            introspection_endpoint,
585            userinfo_endpoint: self.userinfo_endpoint.map(|v| v.into()),
586            response_types_supported: self
587                .response_types_supported
588                .unwrap_or_default()
589                .into_iter() // iterate over Cow<'a, str>
590                .map(|c| c.into_owned())
591                .collect(),
592            response_modes_supported: self
593                .response_modes_supported
594                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
595            scopes_supported: self
596                .scopes_supported
597                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
598            grant_types_supported: self
599                .grant_types_supported
600                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
601            token_endpoint_auth_methods_supported: self
602                .token_endpoint_auth_methods_supported
603                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
604            token_endpoint_auth_signing_alg_values_supported: self
605                .token_endpoint_auth_signing_alg_values_supported
606                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
607            revocation_endpoint_auth_signing_alg_values_supported: self
608                .revocation_endpoint_auth_signing_alg_values_supported
609                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
610            revocation_endpoint_auth_methods_supported: self
611                .revocation_endpoint_auth_methods_supported
612                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
613            introspection_endpoint_auth_methods_supported: self
614                .introspection_endpoint_auth_methods_supported
615                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
616            introspection_endpoint_auth_signing_alg_values_supported: self
617                .introspection_endpoint_auth_signing_alg_values_supported
618                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
619            code_challenge_methods_supported: self
620                .code_challenge_methods_supported
621                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
622            jwks_uri: jwks_uri.clone(),
623            client_id_metadata_document_supported: None,
624        };
625
626        let resource = Self::parse_url_field("resource", self.resource, None)?;
627        let resource_policy_uri =
628            Self::parse_optional_url_field("resource_policy_uri", self.resource_policy_uri, None)?;
629        let resource_tos_uri =
630            Self::parse_optional_url_field("resource_tos_uri", self.resource_tos_uri, None)?;
631
632        // Validate mandatory authorization_servers
633        let authorization_servers =
634            self.authorization_servers
635                .ok_or_else(|| McpSdkError::Internal {
636                    description: "Error: 'authorization_servers' is missing".to_string(),
637                })?;
638        if authorization_servers.is_empty() {
639            return Err(McpSdkError::Internal {
640                description: "Error: 'authorization_servers' must contain at least one URL"
641                    .to_string(),
642            });
643        }
644        let authorization_servers = authorization_servers
645            .iter()
646            .map(|url| {
647                Url::parse(url).map_err(|err| McpSdkError::Internal {
648                    description: format!(
649                        "Error: 'authorization_servers' contains invalid URL '{url}': {err}",
650                    ),
651                })
652            })
653            .collect::<Result<Vec<_>, _>>()?;
654
655        let protected_resource_metadata = OauthProtectedResourceMetadata {
656            resource,
657            authorization_servers,
658            jwks_uri,
659            scopes_supported: self
660                .required_scopes
661                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
662            bearer_methods_supported: self
663                .bearer_methods_supported
664                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
665            resource_signing_alg_values_supported: self
666                .resource_signing_alg_values_supported
667                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
668            resource_name: self.resource_name.map(|s| s.into()),
669            resource_documentation: self.resource_documentation.map(|s| s.into()),
670            resource_policy_uri,
671            resource_tos_uri,
672            tls_client_certificate_bound_access_tokens: self
673                .tls_client_certificate_bound_access_tokens,
674            authorization_details_types_supported: self
675                .authorization_details_types_supported
676                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
677            dpop_signing_alg_values_supported: self
678                .dpop_signing_alg_values_supported
679                .map(|v| v.into_iter().map(|c| c.into_owned()).collect()),
680            dpop_bound_access_tokens_required: self.dpop_bound_access_tokens_required,
681        };
682
683        Ok((authorization_server_metadata, protected_resource_metadata))
684    }
685}