Skip to main content

systemprompt_models/oauth/
protected_resource.rs

1//! RFC 9728 protected-resource metadata.
2//!
3//! One shape for both directions of the handshake: our routes serialise it to
4//! answer `/.well-known/oauth-protected-resource`, and our MCP client
5//! deserialises whatever a server points its `WWW-Authenticate` challenge at.
6//! Keeping a single declaration is what stops the advertised document and the
7//! parsed one drifting apart.
8//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12use serde::{Deserialize, Serialize};
13
14use crate::mcp::McpExtensionId;
15
16#[derive(Debug, Clone, Default, Serialize, Deserialize)]
17pub struct ProtectedResourceMetadata {
18    pub resource: String,
19    #[serde(default)]
20    pub authorization_servers: Vec<String>,
21    #[serde(default)]
22    pub scopes_supported: Vec<String>,
23    #[serde(default)]
24    pub bearer_methods_supported: Vec<String>,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub resource_documentation: Option<String>,
27    /// MCP extensions the resource requires of a client, by extension id.
28    #[serde(default, skip_serializing_if = "Vec::is_empty")]
29    pub mcp_extensions_supported: Vec<McpExtensionId>,
30}
31
32impl ProtectedResourceMetadata {
33    /// Whether the resource requires the Enterprise-Managed Authorization
34    /// flow — an IdP-issued ID-JAG rather than an interactive authorization
35    /// redirect the client may have no user present to complete.
36    #[must_use]
37    pub fn requires_enterprise_managed_auth(&self) -> bool {
38        self.mcp_extensions_supported
39            .contains(&McpExtensionId::EnterpriseManagedAuth)
40    }
41}