systemprompt_models/mcp/
external_auth.rs1use serde::{Deserialize, Serialize};
10
11use crate::errors::ServicesValidationError;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ExternalAuth {
23 pub token_endpoint: String,
24 #[serde(default = "default_auth_header")]
25 pub header: String,
26 #[serde(default = "default_auth_scheme")]
27 pub scheme: String,
28}
29
30fn default_auth_header() -> String {
31 "Authorization".to_owned()
32}
33
34fn default_auth_scheme() -> String {
35 "Bearer".to_owned()
36}
37
38impl ExternalAuth {
39 pub fn header_value(&self, bearer: &str) -> String {
40 if self.scheme.trim().is_empty() {
41 bearer.to_owned()
42 } else {
43 format!("{} {bearer}", self.scheme)
44 }
45 }
46}
47
48pub(super) fn validate_external_auth(
49 name: &str,
50 ext: &ExternalAuth,
51) -> Result<(), ServicesValidationError> {
52 if ext.token_endpoint.starts_with("http://") || ext.token_endpoint.starts_with("https://") {
53 return Err(ServicesValidationError::invalid_field(format!(
54 "MCP server '{name}': external_auth.token_endpoint must be a relative \
55 path (e.g. /api/public/<provider>/token); the host is derived from \
56 server.api_external_url. Remove the scheme+host prefix."
57 )));
58 }
59 if !ext.token_endpoint.starts_with('/') {
60 return Err(ServicesValidationError::invalid_field(format!(
61 "MCP server '{name}': external_auth.token_endpoint must be an absolute \
62 path beginning with '/'."
63 )));
64 }
65 if ext.header.trim().is_empty() {
66 return Err(ServicesValidationError::invalid_field(format!(
67 "MCP server '{name}': external_auth.header must not be empty."
68 )));
69 }
70 Ok(())
71}