Skip to main content

codex_api/
auth.rs

1use codex_client::Request;
2use codex_client::TransportError;
3use http::HeaderMap;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7
8/// Error returned while applying authentication to an outbound request.
9#[derive(Debug, thiserror::Error)]
10pub enum AuthError {
11    #[error("request auth build error: {0}")]
12    Build(String),
13    #[error("transient auth error: {0}")]
14    Transient(String),
15}
16
17impl From<AuthError> for TransportError {
18    fn from(error: AuthError) -> Self {
19        match error {
20            AuthError::Build(message) => TransportError::Build(message),
21            AuthError::Transient(message) => TransportError::Network(message),
22        }
23    }
24}
25
26/// Applies authentication to API requests.
27///
28/// Header-only providers can implement `add_auth_headers`; providers that sign
29/// complete requests can override `apply_auth`.
30pub trait AuthProvider: Send + Sync {
31    /// Adds any auth headers that are available without request body access.
32    ///
33    /// Implementations should be cheap and non-blocking. This method is also
34    /// used by telemetry and non-HTTP request paths.
35    fn add_auth_headers(&self, headers: &mut HeaderMap);
36
37    /// Returns any auth headers that are available without request body access.
38    fn to_auth_headers(&self) -> HeaderMap {
39        let mut headers = HeaderMap::new();
40        self.add_auth_headers(&mut headers);
41        headers
42    }
43
44    /// Applies auth to a complete outbound request and returns the request to send.
45    ///
46    /// The input `request` is moved into this method. Implementations may mutate
47    /// the owned request, or replace it entirely, before returning.
48    ///
49    /// Header-only auth providers can rely on the default implementation.
50    /// Request-signing providers can override this to inspect the final URL,
51    /// headers, and body bytes before the transport sends the request.
52    ///
53    /// Callers must always use the returned request as authoritative.
54    /// If this returns [`AuthError`], the request should not be sent.
55    fn apply_auth(&self, request: Request) -> AuthProviderFuture<'_> {
56        Box::pin(async move {
57            let mut request = request;
58            self.add_auth_headers(&mut request.headers);
59            Ok(request)
60        })
61    }
62}
63
64pub type AuthProviderFuture<'a> =
65    Pin<Box<dyn Future<Output = Result<Request, AuthError>> + Send + 'a>>;
66
67/// Shared auth handle passed through API clients.
68pub type SharedAuthProvider = Arc<dyn AuthProvider>;
69
70#[derive(Clone, Debug, PartialEq, Eq)]
71pub struct AgentIdentityTelemetry {
72    pub agent_id: String,
73    pub task_id: String,
74}
75
76#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
77pub struct AuthHeaderTelemetry {
78    pub attached: bool,
79    pub name: Option<&'static str>,
80}
81
82pub fn auth_header_telemetry(auth: &dyn AuthProvider) -> AuthHeaderTelemetry {
83    let mut headers = HeaderMap::new();
84    auth.add_auth_headers(&mut headers);
85    let name = headers
86        .contains_key(http::header::AUTHORIZATION)
87        .then_some("authorization");
88    AuthHeaderTelemetry {
89        attached: name.is_some(),
90        name,
91    }
92}