car-connectors 0.24.0

Remote MCP connectors for the Common Agent Runtime — connect to remote MCP servers over HTTP, register their tools, and route calls through CAR's governance layer (validator, policy, eventlog).
//! Keychain storage for connector auth-header secrets.
//!
//! Header *values* (bearer tokens, API keys) are stored in the OS
//! keychain under the key `connector:<slug>:<header>`, using the same
//! [`car_secrets`] store `car-auth` uses. The plaintext
//! `connectors.json` only ever records the header *names*.

use car_secrets::{SecretRef, SecretStore};

use crate::error::ConnectorError;

fn key(slug: &str, header: &str) -> String {
    format!("connector:{slug}:{header}")
}

/// Store (or overwrite) a secret auth-header value for a connector.
pub fn put_header_secret(slug: &str, header: &str, value: &str) -> Result<(), ConnectorError> {
    SecretStore::new()
        .put(&SecretRef::with_default_service(key(slug, header)), value)
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

/// Read a stored secret auth-header value.
pub fn get_header_secret(slug: &str, header: &str) -> Result<String, ConnectorError> {
    SecretStore::new()
        .get(&SecretRef::with_default_service(key(slug, header)))
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

/// Delete a stored secret auth-header value (idempotent).
pub fn delete_header_secret(slug: &str, header: &str) -> Result<(), ConnectorError> {
    SecretStore::new()
        .delete(&SecretRef::with_default_service(key(slug, header)))
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

// ---- OAuth token + client-secret storage (Phase 2) ------------------

fn tokens_key(slug: &str) -> String {
    format!("connector:{slug}:tokens")
}

fn client_secret_key(slug: &str) -> String {
    format!("connector:{slug}:client_secret")
}

/// Persist OAuth tokens (JSON) for a connector.
pub fn put_tokens(slug: &str, tokens: &crate::oauth::StoredTokens) -> Result<(), ConnectorError> {
    SecretStore::new()
        .put_json(&SecretRef::with_default_service(tokens_key(slug)), tokens)
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

/// Read stored OAuth tokens, if present.
pub fn get_tokens(slug: &str) -> Result<crate::oauth::StoredTokens, ConnectorError> {
    SecretStore::new()
        .get_json(&SecretRef::with_default_service(tokens_key(slug)))
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

/// Delete stored OAuth tokens (idempotent).
pub fn delete_tokens(slug: &str) -> Result<(), ConnectorError> {
    SecretStore::new()
        .delete(&SecretRef::with_default_service(tokens_key(slug)))
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

/// Store an issued client secret for a connector.
pub fn put_client_secret(slug: &str, secret: &str) -> Result<(), ConnectorError> {
    SecretStore::new()
        .put(&SecretRef::with_default_service(client_secret_key(slug)), secret)
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

/// Read a stored client secret, if present.
pub fn get_client_secret(slug: &str) -> Result<String, ConnectorError> {
    SecretStore::new()
        .get(&SecretRef::with_default_service(client_secret_key(slug)))
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}

/// Delete a stored client secret (idempotent).
pub fn delete_client_secret(slug: &str) -> Result<(), ConnectorError> {
    SecretStore::new()
        .delete(&SecretRef::with_default_service(client_secret_key(slug)))
        .map_err(|e| ConnectorError::Secret(e.to_string()))
}