1use std::path::Path;
2
3use anyhow::Result;
4use inquire::Confirm;
5
6use crate::auth::{server::run_local_auth_server, token::get_auth_user};
7
8pub async fn login(auth_path: &Path, backend_url: &str, frontend_url: &str) -> Result<()> {
9 if let Ok(existing) = get_auth_user(auth_path) {
10 let proceed = Confirm::new(&format!(
11 "Already logged in as @{}. Log in with a different account?",
12 existing.name
13 ))
14 .with_default(false)
15 .prompt()?;
16
17 if !proceed {
18 return Ok(());
19 }
20 }
21
22 let state = generate_state_token();
23 open::that(format!("{}/auth/cli-login?state={}", backend_url, state))?;
27 println!("Go to your browser for further authorization");
28 let user = run_local_auth_server(state, frontend_url).await?;
29
30 let auth_json = serde_json::to_string(&user)?;
31 write_auth_file(auth_path, &auth_json)?;
32
33 println!("✅ Authorization successful as @{}", user.name);
34
35 Ok(())
36}
37
38fn generate_state_token() -> String {
40 uuid::Uuid::new_v4().simple().to_string()
41}
42
43fn write_auth_file(path: &Path, content: &str) -> Result<()> {
46 #[cfg(unix)]
47 {
48 use std::io::Write;
49 use std::os::unix::fs::OpenOptionsExt;
50 let mut file = std::fs::OpenOptions::new()
51 .write(true)
52 .create(true)
53 .truncate(true)
54 .mode(0o600)
55 .open(path)?;
56 file.write_all(content.as_bytes())?;
57 }
58 #[cfg(not(unix))]
59 {
60 std::fs::write(path, content)?;
61 }
62 Ok(())
63}