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