bw/auth/
login.rs

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        // TODO: We should build a web captcha solution
33        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            // Send token
49            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}