aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.eu>2023-03-19 21:52:54 +0100
committerRené 'Necoro' Neumann <necoro@necoro.eu>2023-03-19 21:52:54 +0100
commita1105320acfc8e48c707f2870ad2ba9ea7480bf4 (patch)
treec1de5822c8010191d1c327112fba7a3a2033d41e
parent0509f3070dd66f5e770dd970f7def6878a57ddd8 (diff)
downloadrbw-a1105320acfc8e48c707f2870ad2ba9ea7480bf4.tar.gz
rbw-a1105320acfc8e48c707f2870ad2ba9ea7480bf4.zip
Add support for 2FA-method 'Email'.
Generalize the `two_factor` function to allow for different Providers. The `login` function now holds a list of supported providers that it tests in turn. The list should probably adhere to https://bitwarden.com/help/setup-two-step-login/#using-multiple-methods. Closes #90.
-rw-r--r--src/api.rs25
-rw-r--r--src/bin/rbw-agent/actions.rs71
2 files changed, 63 insertions, 33 deletions
diff --git a/src/api.rs b/src/api.rs
index 174949d..f2c853d 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -57,6 +57,31 @@ pub enum TwoFactorProviderType {
WebAuthn = 7,
}
+impl TwoFactorProviderType {
+ pub fn message(&self) -> &str {
+ match *self {
+ TwoFactorProviderType::Authenticator => "Enter the 6 digit verification code from your authenticator app.",
+ TwoFactorProviderType::Email => "Enter the PIN you received via email.",
+ _ => "Enter the code."
+ }
+ }
+
+ pub fn header(&self) -> &str {
+ match *self {
+ TwoFactorProviderType::Authenticator => "Authenticator App",
+ TwoFactorProviderType::Email => "Email Code",
+ _ => "Two Factor Authentication"
+ }
+ }
+
+ pub fn grab(&self) -> bool {
+ match *self {
+ TwoFactorProviderType::Email => false,
+ _ => true
+ }
+ }
+}
+
impl<'de> serde::Deserialize<'de> for TwoFactorProviderType {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs
index fff34a5..88236ba 100644
--- a/src/bin/rbw-agent/actions.rs
+++ b/src/bin/rbw-agent/actions.rs
@@ -99,7 +99,7 @@ pub async fn login(
let email = config_email().await?;
let mut err_msg = None;
- for i in 1_u8..=3 {
+ 'attempts: for i in 1_u8..=3 {
let err = if i > 1 {
// this unwrap is safe because we only ever continue the loop
// if we have set err_msg
@@ -138,37 +138,42 @@ pub async fn login(
email,
)
.await?;
- break;
+ break 'attempts;
}
Err(rbw::error::Error::TwoFactorRequired { providers }) => {
- if providers.contains(
- &rbw::api::TwoFactorProviderType::Authenticator,
- ) {
- let (
- access_token,
- refresh_token,
- iterations,
- protected_key,
- ) = two_factor(
- tty,
- &email,
- password.clone(),
- rbw::api::TwoFactorProviderType::Authenticator,
- )
- .await?;
- login_success(
- sock,
- state,
- access_token,
- refresh_token,
- iterations,
- protected_key,
- password,
- db,
- email,
- )
- .await?;
- break;
+ let supported_types = vec![
+ rbw::api::TwoFactorProviderType::Authenticator,
+ rbw::api::TwoFactorProviderType::Email,
+ ];
+
+ for provider in supported_types.into_iter() {
+ if providers.contains(&provider) {
+ let (
+ access_token,
+ refresh_token,
+ iterations,
+ protected_key,
+ ) = two_factor(
+ tty,
+ &email,
+ password.clone(),
+ provider
+ )
+ .await?;
+ login_success(
+ sock,
+ state,
+ access_token,
+ refresh_token,
+ iterations,
+ protected_key,
+ password,
+ db,
+ email,
+ )
+ .await?;
+ break 'attempts;
+ }
}
return Err(anyhow::anyhow!("TODO"));
}
@@ -212,11 +217,11 @@ async fn two_factor(
};
let code = rbw::pinentry::getpin(
&config_pinentry().await?,
- "Authenticator App",
- "Enter the 6 digit verification code from your authenticator app.",
+ provider.header(),
+ provider.message(),
err.as_deref(),
tty,
- true,
+ provider.grab(),
)
.await
.context("failed to read code from pinentry")?;