Skip to main content

smbcloud_ascapi_frontend/
env.rs

1//! Credential resolution from the environment.
2
3use smbcloud_ascapi_core::ApiKey;
4use smbcloud_ascapi_core::Error;
5
6/// Resolve an App Store Connect API key from the environment.
7///
8/// Deliberately per-call rather than at startup: an MCP server that
9/// refuses to start without credentials cannot answer `tools/list`, which
10/// is the first thing every client asks and the only way a user discovers
11/// what configuration is missing. Unconfigured servers should list their
12/// tools and fail with a useful message when one is called.
13pub fn api_key_from_env() -> Result<ApiKey, String> {
14    let key_id = std::env::var("ASC_API_KEY")
15        .map_err(|_| "ASC_API_KEY is not set (App Store Connect API key ID)".to_string())?;
16    let issuer_id = std::env::var("ASC_ISSUER_ID")
17        .map_err(|_| "ASC_ISSUER_ID is not set (App Store Connect issuer ID)".to_string())?;
18
19    let path = match std::env::var("ASC_PRIVATE_KEY_PATH") {
20        Ok(path) => std::path::PathBuf::from(path),
21        Err(_) => {
22            let home = std::env::var("HOME").map_err(|_| {
23                "neither ASC_PRIVATE_KEY_PATH nor HOME is set, so the .p8 key cannot be located"
24                    .to_string()
25            })?;
26            std::path::PathBuf::from(home)
27                .join(".appstoreconnect")
28                .join("private_keys")
29                .join(format!("AuthKey_{key_id}.p8"))
30        }
31    };
32
33    ApiKey::from_p8_file(&key_id, &issuer_id, &path).map_err(|error| match error {
34        // The most common misconfiguration by a wide margin, and the
35        // default-path message needs to name the path it guessed.
36        Error::PrivateKeyRead { .. } => {
37            format!(
38                "could not read the App Store Connect private key at {}",
39                path.display()
40            )
41        }
42        other => other.to_string(),
43    })
44}