Skip to main content

bw/auth/
login.rs

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            // Send token
46            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}