Skip to main content

systemprompt_api/services/proxy/auth/
mod.rs

1//! Proxy authentication and authorization for backend service access.
2//!
3//! Two cohesive halves:
4//!
5//! - `challenge`: validating credential presence and building the RFC 6750 /
6//!   RFC 9728 `WWW-Authenticate` 401/403 OAuth challenges that drive MCP and
7//!   agent clients into the OAuth discovery flow.
8//! - `access`: resolving a service's OAuth requirement from the agent / MCP
9//!   registries and enforcing the required scopes against the authenticated
10//!   user, with the session-cache fallback for already-established MCP
11//!   sessions.
12
13mod access;
14mod challenge;
15
16pub(crate) use access::{AccessValidator, mcp_oauth_requirement};
17pub use challenge::OAuthChallengeBuilder;
18pub(crate) use challenge::build_mcp_unknown_service_challenge;
19
20#[cfg(feature = "test-api")]
21pub mod test_api {
22    use axum::http::HeaderMap;
23    use axum::response::{IntoResponse, Response};
24    use systemprompt_models::RequestContext;
25    use systemprompt_models::auth::AuthenticatedUser;
26    use systemprompt_runtime::AppContext;
27
28    #[derive(Debug)]
29    pub struct Requirement {
30        pub module: String,
31        pub required: bool,
32        pub scopes: Vec<String>,
33        pub audience: String,
34    }
35
36    pub fn validate_with_requirement(
37        headers: &HeaderMap,
38        service_name: &str,
39        requirement: &Requirement,
40        ctx: &AppContext,
41        req_context: Option<&RequestContext>,
42    ) -> Result<Option<AuthenticatedUser>, Box<Response>> {
43        let internal = super::access::OAuthRequirement {
44            module: requirement.module.clone(),
45            required: requirement.required,
46            scopes: requirement.scopes.clone(),
47            audience: requirement.audience.clone(),
48        };
49        super::access::AccessValidator::validate_with_requirement(
50            headers,
51            service_name,
52            &internal,
53            ctx,
54            req_context,
55        )
56        .map_err(|e| Box::new(e.into_response()))
57    }
58}