vtcode-auth 0.143.2

Authentication and OAuth flows shared across VT Code
Documentation
use anyhow::{Context, Result, anyhow};
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
#[cfg(test)]
use std::sync::{LazyLock, Mutex};
use tempfile::Builder;

#[cfg(test)]
static AUTH_DIR_OVERRIDE: LazyLock<Mutex<Option<PathBuf>>> = LazyLock::new(|| Mutex::new(None));

pub(crate) fn auth_storage_dir() -> Result<PathBuf> {
    #[cfg(test)]
    if let Some(path) = AUTH_DIR_OVERRIDE
        .lock()
        .map_err(|_| anyhow!("auth storage override mutex poisoned"))?
        .clone()
    {
        fs::create_dir_all(&path).context("failed to create auth directory")?;
        set_private_directory_permissions(&path)?;
        return Ok(path);
    }

    use std::sync::OnceLock;
    use std::sync::atomic::{AtomicBool, Ordering};

    static CACHED_PATH: OnceLock<PathBuf> = OnceLock::new();
    static DIR_CREATED: AtomicBool = AtomicBool::new(false);

    // Use cached path if already resolved (fast path, no fs access).
    if let Some(dir) = CACHED_PATH.get() {
        if !DIR_CREATED.load(Ordering::Acquire) {
            fs::create_dir_all(dir).context("failed to create auth directory")?;
            DIR_CREATED.store(true, Ordering::Release);
        }
        return Ok(dir.clone());
    }

    // First call: resolve the path, ensure it exists, then cache it.
    let auth_dir = dirs::home_dir()
        .ok_or_else(|| anyhow!("could not determine home directory"))?
        .join(".vtcode")
        .join("auth");

    fs::create_dir_all(&auth_dir).context("failed to create auth directory")?;
    set_private_directory_permissions(&auth_dir)?;

    // Cache for subsequent calls. If another thread set it first, discard ours.
    drop(CACHED_PATH.set(auth_dir.clone()));
    DIR_CREATED.store(true, Ordering::Release);

    Ok(auth_dir)
}

pub(crate) fn legacy_auth_storage_path() -> Result<PathBuf> {
    #[cfg(test)]
    if let Some(path) = AUTH_DIR_OVERRIDE
        .lock()
        .map_err(|_| anyhow!("auth storage override mutex poisoned"))?
        .clone()
    {
        fs::create_dir_all(&path).context("failed to create auth directory")?;
        set_private_directory_permissions(&path)?;
        return Ok(path.join("auth.json"));
    }

    // Reuse the auth_storage_dir parent so the home-dir + .vtcode resolution
    // is not duplicated.
    let auth_dir = auth_storage_dir()?;
    let vtcode_dir = auth_dir.parent().ok_or_else(|| anyhow!("auth storage has no parent"))?;
    Ok(vtcode_dir.join("auth.json"))
}

pub(crate) fn write_private_file(path: &Path, contents: &[u8]) -> Result<()> {
    let parent = path
        .parent()
        .ok_or_else(|| anyhow!("private file path {} has no parent directory", path.display()))?;
    let mut temp = Builder::new()
        .prefix(".tmp.")
        .tempfile_in(parent)
        .with_context(|| format!("failed to create temporary file in {}", parent.display()))?;

    #[cfg(unix)]
    set_private_permissions(temp.as_file(), temp.path())?;
    temp.as_file_mut()
        .write_all(contents)
        .with_context(|| format!("failed to write private file {}", path.display()))?;
    temp.as_file()
        .sync_all()
        .with_context(|| format!("failed to sync private file {}", path.display()))?;

    let _persisted = temp
        .persist(path)
        .with_context(|| format!("failed to persist private file {}", path.display()))?;
    #[cfg(unix)]
    set_private_path_permissions(path)?;
    Ok(())
}

#[cfg(unix)]
fn set_private_directory_permissions(path: &Path) -> Result<()> {
    use std::os::unix::fs::PermissionsExt;

    fs::set_permissions(path, fs::Permissions::from_mode(0o700))
        .with_context(|| format!("failed to set permissions on {}", path.display()))
}

#[cfg(not(unix))]
fn set_private_directory_permissions(_path: &Path) -> Result<()> {
    Ok(())
}

#[cfg(unix)]
fn set_private_permissions(file: &fs::File, path: &Path) -> Result<()> {
    use std::os::unix::fs::PermissionsExt;

    file.set_permissions(fs::Permissions::from_mode(0o600))
        .with_context(|| format!("failed to set permissions on {}", path.display()))
}

#[cfg(unix)]
fn set_private_path_permissions(path: &Path) -> Result<()> {
    use std::os::unix::fs::PermissionsExt;

    fs::set_permissions(path, fs::Permissions::from_mode(0o600))
        .with_context(|| format!("failed to set permissions on {}", path.display()))
}

#[cfg(test)]
pub(crate) fn set_auth_storage_dir_override_for_tests(path: Option<PathBuf>) -> Result<()> {
    let mut override_path = AUTH_DIR_OVERRIDE
        .lock()
        .map_err(|_| anyhow!("auth storage override mutex poisoned"))?;
    *override_path = path;
    Ok(())
}

#[cfg(test)]
pub(crate) fn auth_storage_dir_override_for_tests() -> Result<Option<PathBuf>> {
    AUTH_DIR_OVERRIDE
        .lock()
        .map_err(|_| anyhow!("auth storage override mutex poisoned"))
        .map(|path| path.clone())
}