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