bitbucket_cli/cli/
auth.rs

1use anyhow::Result;
2use clap::Subcommand;
3use colored::Colorize;
4
5use crate::auth::{AppPasswordAuth, AuthManager};
6use crate::config::Config;
7
8#[derive(Subcommand)]
9pub enum AuthCommands {
10    /// Authenticate with Bitbucket
11    Login {
12        /// Use app password authentication (default)
13        #[arg(long, default_value = "true")]
14        app_password: bool,
15    },
16
17    /// Remove stored credentials
18    Logout,
19
20    /// Show authentication status
21    Status,
22}
23
24impl AuthCommands {
25    pub async fn run(self) -> Result<()> {
26        match self {
27            AuthCommands::Login { app_password: _ } => {
28                // For now, only app password auth is fully implemented
29                let auth_manager = AuthManager::new()?;
30                let credential = AppPasswordAuth::authenticate(&auth_manager).await?;
31
32                // Save username to config
33                if let Some(username) = credential.username() {
34                    let mut config = Config::load()?;
35                    config.set_username(username);
36                    config.save()?;
37                }
38
39                Ok(())
40            }
41
42            AuthCommands::Logout => {
43                let auth_manager = AuthManager::new()?;
44                auth_manager.clear_credentials()?;
45
46                let mut config = Config::load()?;
47                config.clear_auth();
48                config.save()?;
49
50                println!("{} Logged out successfully", "✓".green());
51                Ok(())
52            }
53
54            AuthCommands::Status => {
55                let auth_manager = AuthManager::new()?;
56                let config = Config::load()?;
57
58                if auth_manager.is_authenticated() {
59                    println!("{} Authenticated", "✓".green());
60
61                    if let Some(username) = config.username() {
62                        println!("  {} {}", "Username:".dimmed(), username);
63                    }
64
65                    if let Some(workspace) = config.default_workspace() {
66                        println!("  {} {}", "Workspace:".dimmed(), workspace);
67                    }
68
69                    // Test the credentials
70                    match crate::api::BitbucketClient::from_stored() {
71                        Ok(client) => match client.get::<serde_json::Value>("/user").await {
72                            Ok(user) => {
73                                if let Some(display_name) = user.get("display_name") {
74                                    println!(
75                                        "  {} {}",
76                                        "Display name:".dimmed(),
77                                        display_name.as_str().unwrap_or("Unknown")
78                                    );
79                                }
80                            }
81                            Err(e) => {
82                                println!("{} Credentials may be invalid: {}", "⚠".yellow(), e);
83                            }
84                        },
85                        Err(e) => {
86                            println!("{} Failed to create client: {}", "✗".red(), e);
87                        }
88                    }
89                } else {
90                    println!("{} Not authenticated", "✗".red());
91                    println!();
92                    println!("Run {} to authenticate", "bitbucket auth login".cyan());
93                }
94
95                Ok(())
96            }
97        }
98    }
99}