From a1105320acfc8e48c707f2870ad2ba9ea7480bf4 Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Sun, 19 Mar 2023 21:52:54 +0100 Subject: 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. --- src/api.rs | 25 ++++++++++++++++ src/bin/rbw-agent/actions.rs | 71 ++++++++++++++++++++++++-------------------- 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(deserializer: D) -> std::result::Result 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")?; -- cgit v1.2.3-54-g00ecf