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