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 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!("Two factor code sent to {tf:?}");
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 kdf,
73 })
74 .await?;
75
76 debug!("{result:?}");
77 } else {
78 debug!("{result:?}");
79 }
80
81 let res = client
82 .vault()
83 .sync(&SyncRequest {
84 exclude_subdomains: Some(true),
85 })
86 .await?;
87 info!("{res:#?}");
88
89 Ok(())
90}
91
92pub(crate) async fn login_api_key(
93 client: Client,
94 client_id: Option<String>,
95 client_secret: Option<String>,
96) -> Result<()> {
97 let client_id = text_prompt_when_none("Client ID", client_id)?;
98 let client_secret = text_prompt_when_none("Client Secret", client_secret)?;
99
100 let password = Password::new("Password").without_confirmation().prompt()?;
101
102 let result = client
103 .auth()
104 .login_api_key(&ApiKeyLoginRequest {
105 client_id,
106 client_secret,
107 password,
108 })
109 .await?;
110
111 debug!("{result:?}");
112
113 Ok(())
114}
115
116pub(crate) async fn login_device(
117 client: Client,
118 email: Option<String>,
119 device_identifier: Option<String>,
120) -> Result<()> {
121 let email = text_prompt_when_none("Email", email)?;
122 let device_identifier = text_prompt_when_none("Device Identifier", device_identifier)?;
123
124 let auth = client.auth().login_device(email, device_identifier).await?;
125
126 println!("Fingerprint: {}", auth.fingerprint);
127
128 Text::new("Press enter once approved").prompt()?;
129
130 client.auth().login_device_complete(auth).await?;
131
132 Ok(())
133}