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//!
13//! Copyright (c) systemprompt.io — Business Source License 1.1.
14//! See <https://systemprompt.io> for licensing details.
15
16mod access;
17mod challenge;
18
19pub(crate) use access::{AccessValidator, mcp_oauth_requirement};
20pub use challenge::OAuthChallengeBuilder;
21pub(crate) use challenge::build_mcp_unknown_service_challenge;
22
23#[cfg(feature = "test-api")]
24pub mod test_api {
25    use axum::http::HeaderMap;
26    use axum::response::{IntoResponse, Response};
27    use systemprompt_models::RequestContext;
28    use systemprompt_models::auth::AuthenticatedUser;
29    use systemprompt_runtime::AppContext;
30
31    #[derive(Debug)]
32    pub struct Requirement {
33        pub module: String,
34        pub required: bool,
35        pub scopes: Vec<String>,
36        pub audience: String,
37    }
38
39    pub fn validate_with_requirement(
40        headers: &HeaderMap,
41        service_name: &str,
42        requirement: &Requirement,
43        ctx: &AppContext,
44        req_context: Option<&RequestContext>,
45    ) -> Result<Option<AuthenticatedUser>, Box<Response>> {
46        let internal = super::access::OAuthRequirement {
47            module: requirement.module.clone(),
48            required: requirement.required,
49            scopes: requirement.scopes.clone(),
50            audience: requirement.audience.clone(),
51        };
52        super::access::AccessValidator::validate_with_requirement(
53            headers,
54            service_name,
55            &internal,
56            ctx,
57            req_context,
58        )
59        .map_err(|e| Box::new(e.into_response()))
60    }
61}