1use bitwarden_cli::text_prompt_when_none;
2use bitwarden_core::{
3 Client,
4 auth::login::{
5 ApiKeyLoginRequest, PasswordLoginRequest, TwoFactorEmailRequest, TwoFactorProvider,
6 TwoFactorRequest,
7 },
8};
9use bitwarden_sync::{SyncClientExt, SyncRequest};
10use color_eyre::eyre::{Result, bail};
11use inquire::{Password, Text};
12use tracing::{debug, error, info};
13
14pub(crate) async fn login_password(client: Client, email: Option<String>) -> Result<()> {
15 let email = text_prompt_when_none("Email", email)?;
16
17 let password = match std::env::var("BW_PASSWORD") {
18 Ok(pw) => pw,
19 Err(_) => Password::new("Password").without_confirmation().prompt()?,
20 };
21
22 let result = client
23 .auth()
24 .login_password(&PasswordLoginRequest {
25 email: email.clone(),
26 password: password.clone(),
27 two_factor: None,
28 })
29 .await?;
30
31 if let Some(two_factor) = result.two_factor {
32 error!(?two_factor);
33
34 let two_factor = if let Some(tf) = two_factor.authenticator {
35 debug!(?tf);
36
37 let token = Text::new("Authenticator code").prompt()?;
38
39 Some(TwoFactorRequest {
40 token,
41 provider: TwoFactorProvider::Authenticator,
42 remember: false,
43 })
44 } else if let Some(tf) = two_factor.email {
45 client
47 .auth()
48 .send_two_factor_email(&TwoFactorEmailRequest {
49 email: email.clone(),
50 password: password.clone(),
51 })
52 .await?;
53
54 info!(?tf, "Two factor code sent to");
55 let token = Text::new("Two factor code").prompt()?;
56
57 Some(TwoFactorRequest {
58 token,
59 provider: TwoFactorProvider::Email,
60 remember: false,
61 })
62 } else {
63 bail!("Not supported: {:?}", two_factor);
64 };
65
66 let result = client
67 .auth()
68 .login_password(&PasswordLoginRequest {
69 email,
70 password,
71 two_factor,
72 })
73 .await?;
74
75 debug!(?result);
76 } else {
77 debug!(?result);
78 }
79
80 let res = client
81 .sync()
82 .sync(SyncRequest {
83 exclude_subdomains: None,
84 })
85 .await?;
86 info!(?res);
87
88 Ok(())
89}
90
91pub(crate) async fn login_api_key(
92 client: Client,
93 client_id: Option<String>,
94 client_secret: Option<String>,
95) -> Result<()> {
96 let client_id = text_prompt_when_none("Client ID", client_id)?;
97 let client_secret = text_prompt_when_none("Client Secret", client_secret)?;
98
99 let password = match std::env::var("BW_PASSWORD") {
100 Ok(pw) => pw,
101 Err(_) => Password::new("Password").without_confirmation().prompt()?,
102 };
103
104 let result = client
105 .auth()
106 .login_api_key(&ApiKeyLoginRequest {
107 client_id,
108 client_secret,
109 password,
110 })
111 .await?;
112
113 debug!(?result);
114
115 Ok(())
116}
117
118pub(crate) async fn login_device(
119 client: Client,
120 email: Option<String>,
121 device_identifier: Option<String>,
122) -> Result<()> {
123 let email = text_prompt_when_none("Email", email)?;
124 let device_identifier = text_prompt_when_none("Device Identifier", device_identifier)?;
125
126 let auth = client.auth().login_device(email, device_identifier).await?;
127
128 println!("Fingerprint: {}", auth.fingerprint);
129
130 Text::new("Press enter once approved").prompt()?;
131
132 client.auth().login_device_complete(auth).await?;
133
134 Ok(())
135}