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