mecha10-auth 0.8.0

Authentication services for Mecha10 - shared between CLI and launcher
Documentation
//! Mecha10 Authentication Library
//!
//! Shared authentication services for CLI and launcher.
//!
//! ## Features
//!
//! - Device code OAuth 2.0 flow (RFC 8628) for browser-based authentication
//! - Credentials storage at ~/.mecha10/credentials.json
//! - Support for custom auth server URLs via MECHA10_AUTH_URL environment variable
//!
//! ## Usage
//!
//! ```rust,no_run
//! use mecha10_auth::{AuthService, CredentialsService};
//!
//! async fn login() -> anyhow::Result<()> {
//!     let auth_service = AuthService::new();
//!     let credentials_service = CredentialsService::new();
//!
//!     // Run device code flow
//!     let credentials = auth_service.run_device_code_flow(|device_code| {
//!         println!("Go to: {}", device_code.verification_uri);
//!         println!("Enter code: {}", device_code.user_code);
//!     }).await?;
//!
//!     // Save credentials
//!     credentials_service.save(&credentials)?;
//!     Ok(())
//! }
//! ```

#![allow(
    clippy::print_stdout,
    clippy::print_stderr,
    reason = "auth commands print interactive login/status output directly to the terminal for the CLI and launcher, by design"
)]

pub mod commands;
mod credentials;
mod service;
pub mod types;

pub use commands::ensure_authenticated_interactive;
pub use credentials::{default_credentials_path, CredentialsService};
pub use service::AuthService;
pub use types::{AuthError, Credentials, DeviceCodeResponse, DeviceCodeStatus};

pub const DEFAULT_AUTH_URL: &str = "https://mecha.industries/api/auth";

pub const AUTH_URL_ENV_VAR: &str = "MECHA10_AUTH_URL";

/// Get the auth URL, checking environment variable first
///
/// Priority:
/// 1. MECHA10_AUTH_URL environment variable
/// 2. DEFAULT_AUTH_URL constant
pub fn get_auth_url() -> String {
    mecha10_core::config::env_or_default(AUTH_URL_ENV_VAR, DEFAULT_AUTH_URL)
}

/// Display the device code flow instructions in a formatted box
pub fn display_device_code_instructions(device_code: &DeviceCodeResponse) {
    println!("┌────────────────────────────────────────────────────────┐");
    println!("│  Open this URL in your browser:                        │");
    println!("│                                                        │");
    println!("│    {:<50}  │", device_code.verification_uri);
    println!("│                                                        │");
    println!("│  Then enter this code:                                 │");
    println!("│                                                        │");
    println!(
        "│    {}                                        │",
        device_code.user_code
    );
    println!("│                                                        │");
    println!("└────────────────────────────────────────────────────────┘");
    println!();
    println!("Waiting for browser authorization...");
    println!("(You can use a different device if this machine has no browser)");
}

/// Try to open the verification URL in the default browser
pub fn open_browser(url: &str) {
    let _ = open::that(url);
}