ez-token 0.1.0

CLI tool for generating OAuth2 access tokens via PKCE and Client Credentials for Microsoft Entra ID and Auth0
Documentation
use crate::cli::output::print_header;
use crate::cli::{args::AuthArgs, auth_params::AuthParams};
use crate::config::cli_config::Profile;
use crate::services::authentication::authenticator::execute_flow;
use crate::services::authentication::client_credentials::ClientCredentialsFlow;
use dialoguer::{Password, theme::ColorfulTheme};
use miette::{IntoDiagnostic, Result};

/// Runs the Client Credentials (machine-to-machine) authentication flow.
///
/// Resolves authentication parameters from CLI arguments, the active profile,
/// or interactive prompts. The client secret is always handled securely —
/// if not provided via `--client-secret`, it is prompted interactively
/// and is never persisted to disk.
///
/// On success, the access token is copied to the clipboard automatically.
///
/// # Arguments
///
/// * `profile` - The active configuration profile loaded from disk.
/// * `args` - Parsed CLI arguments for tenant, client, and scopes.
/// * `arg_secret` - Optional client secret passed via `--client-secret`.
///
/// # Errors
///
/// Returns an error if:
/// - Any required parameter cannot be resolved.
/// - The interactive secret prompt fails (e.g., in a non-interactive terminal).
/// - The token exchange with Entra ID fails.
pub async fn run(profile: Profile, args: AuthArgs, arg_secret: Option<String>) -> Result<()> {
    let theme = ColorfulTheme::default();
    print_header("OAuth2 Token Generator (M2M)");

    let auth = AuthParams::new(&profile, args, "api://<client_id>/.default")?;

    let client_secret = match arg_secret {
        Some(s) => s,
        None => Password::with_theme(&theme)
            .with_prompt("Enter Client Secret")
            .interact()
            .into_diagnostic()?,
    };

    let flow = ClientCredentialsFlow {
        provider: auth.provider,
        client_id: auth.client_id,
        client_secret,
        scopes: auth.scopes,
    };

    execute_flow(&flow).await?;
    Ok(())
}