use serde_json::Value;
use crate::config::ResolvedOAuthConfig;
pub const PROTECTED_RESOURCE_METADATA_PREFIX: &str = "/.well-known/oauth-protected-resource";
pub(crate) fn resource_metadata_url(resource: &str) -> String {
let trimmed = resource.trim();
let Some((scheme, rest)) = trimmed.split_once("://") else {
return format!(
"{}{PROTECTED_RESOURCE_METADATA_PREFIX}",
trimmed.trim_end_matches('/')
);
};
let (authority, path) = match rest.find('/') {
Some(i) if &rest[i..] == "/" => (&rest[..i], ""),
Some(i) => (&rest[..i], &rest[i..]),
None => (rest, ""),
};
format!("{scheme}://{authority}{PROTECTED_RESOURCE_METADATA_PREFIX}{path}")
}
pub(crate) fn metadata_path(metadata_url: &str) -> String {
metadata_url
.split_once("://")
.and_then(|(_, rest)| rest.find('/').map(|i| rest[i..].to_string()))
.unwrap_or_else(|| PROTECTED_RESOURCE_METADATA_PREFIX.to_string())
}
pub(crate) fn metadata_document(config: &ResolvedOAuthConfig) -> Value {
let mut doc = serde_json::json!({
"resource": config.resource,
"authorization_servers": [config.issuer],
"bearer_methods_supported": ["header"],
});
if !config.scopes_supported.is_empty() {
doc["scopes_supported"] = serde_json::json!(config.scopes_supported);
}
if let Some(name) = &config.resource_name {
doc["resource_name"] = Value::String(name.clone());
}
doc
}
pub(crate) fn invalid_token(resource_metadata_url: &str, supported_scopes: &str) -> String {
if supported_scopes.is_empty() {
return format!(
"Bearer error=\"invalid_token\", resource_metadata=\"{}\"",
quoted(resource_metadata_url)
);
}
format!(
"Bearer error=\"invalid_token\", resource_metadata=\"{}\", scope=\"{}\"",
quoted(resource_metadata_url),
quoted(supported_scopes)
)
}
pub(crate) fn insufficient_scope(required_scopes: &str, resource_metadata_url: &str) -> String {
if required_scopes.is_empty() {
return format!(
"Bearer error=\"insufficient_scope\", resource_metadata=\"{}\"",
quoted(resource_metadata_url)
);
}
format!(
"Bearer error=\"insufficient_scope\", scope=\"{}\", resource_metadata=\"{}\"",
quoted(required_scopes),
quoted(resource_metadata_url)
)
}
pub(crate) fn quoted(value: &str) -> String {
value.replace('\\', "\\\\").replace('"', "\\\"")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn metadata_url_splices_the_well_known_segment_before_the_path() {
assert_eq!(
resource_metadata_url("https://kb.example.com/mcp"),
"https://kb.example.com/.well-known/oauth-protected-resource/mcp"
);
}
#[test]
fn metadata_url_for_a_path_less_resource_is_the_bare_well_known() {
assert_eq!(
resource_metadata_url("https://kb.example.com"),
"https://kb.example.com/.well-known/oauth-protected-resource"
);
assert_eq!(
resource_metadata_url("https://kb.example.com/"),
"https://kb.example.com/.well-known/oauth-protected-resource"
);
}
#[test]
fn metadata_url_keeps_a_port_and_the_path_verbatim() {
assert_eq!(
resource_metadata_url("http://localhost:8001/mcp"),
"http://localhost:8001/.well-known/oauth-protected-resource/mcp"
);
assert_eq!(
resource_metadata_url("http://localhost:8001/mcp/"),
"http://localhost:8001/.well-known/oauth-protected-resource/mcp/"
);
}
#[test]
fn metadata_url_of_a_malformed_resource_does_not_panic() {
assert_eq!(
resource_metadata_url("kb.example.com/mcp"),
"kb.example.com/mcp/.well-known/oauth-protected-resource"
);
}
#[test]
fn metadata_path_is_the_route_to_serve() {
for (resource, path) in [
(
"https://kb.example.com/mcp",
"/.well-known/oauth-protected-resource/mcp",
),
(
"https://kb.example.com/api/v1/",
"/.well-known/oauth-protected-resource/api/v1/",
),
(
"https://kb.example.com",
"/.well-known/oauth-protected-resource",
),
(
"kb.example.com/mcp",
"/.well-known/oauth-protected-resource",
),
] {
assert_eq!(
metadata_path(&resource_metadata_url(resource)),
path,
"{resource}"
);
}
}
#[test]
fn challenge_values_are_escaped_not_pasted() {
assert_eq!(quoted(r#"a"b\c"#), r#"a\"b\\c"#);
}
#[test]
fn an_invalid_token_challenge_with_no_advertised_scope_omits_scope() {
assert_eq!(
invalid_token("https://x/.well-known/oauth-protected-resource", ""),
"Bearer error=\"invalid_token\", \
resource_metadata=\"https://x/.well-known/oauth-protected-resource\""
);
assert_eq!(
invalid_token("https://x/.well-known/oauth-protected-resource", "a b"),
"Bearer error=\"invalid_token\", \
resource_metadata=\"https://x/.well-known/oauth-protected-resource\", scope=\"a b\""
);
}
#[test]
fn an_insufficient_scope_challenge_with_no_required_scope_omits_scope() {
assert_eq!(
insufficient_scope("", "https://x/.well-known/oauth-protected-resource"),
"Bearer error=\"insufficient_scope\", \
resource_metadata=\"https://x/.well-known/oauth-protected-resource\""
);
}
}