-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add rudimentary 2FA support #11
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, esp. considering that you don't have much Rust experience (needs a CI check though). Thanks a lot :)
#[clap(long, default_value_t = 0.to_string(), env = "TUTANOTA_CLI_ONETIMECODE")] | ||
onetimecode: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[clap(long, default_value_t = 0.to_string(), env = "TUTANOTA_CLI_ONETIMECODE")] | |
onetimecode: String, | |
#[clap(long, env = "TUTANOTA_CLI_ONETIMECODE")] | |
onetimecode: Option<String>, |
Should be optional. clap will default this to None
and not complain if the env var (or the CLI parameter) is missing.
// loggedin tracks if timeout loop exited due to successful login or due to 2FA detection | ||
let mut loggedin = false; | ||
|
||
// Check if login successful or if 2FA required (Cancel button appears) | ||
tokio::time::timeout(Duration::from_secs(20), async { | ||
loop { | ||
if has_new_email_button(webdriver) | ||
.await | ||
.context("search new-email button")? | ||
{ | ||
loggedin = true; | ||
return Ok::<_, anyhow::Error>(()); // Login successful | ||
} | ||
else if has_cancel_button(webdriver) | ||
.await | ||
.context("search cancel button")? | ||
{ | ||
// Cancel button present means 2FA needed | ||
enter_onetimecode(&onetimecode, webdriver) | ||
.await | ||
.context("entering one-time-code and finishing login")?; | ||
return Ok::<_, anyhow::Error>(()); // 2FA entered, logging in... | ||
} | ||
|
||
tokio::time::sleep(Duration::from_secs(1)).await; | ||
} | ||
}) | ||
.await | ||
.context("wait for one-time-code or login")??; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// loggedin tracks if timeout loop exited due to successful login or due to 2FA detection | |
let mut loggedin = false; | |
// Check if login successful or if 2FA required (Cancel button appears) | |
tokio::time::timeout(Duration::from_secs(20), async { | |
loop { | |
if has_new_email_button(webdriver) | |
.await | |
.context("search new-email button")? | |
{ | |
loggedin = true; | |
return Ok::<_, anyhow::Error>(()); // Login successful | |
} | |
else if has_cancel_button(webdriver) | |
.await | |
.context("search cancel button")? | |
{ | |
// Cancel button present means 2FA needed | |
enter_onetimecode(&onetimecode, webdriver) | |
.await | |
.context("entering one-time-code and finishing login")?; | |
return Ok::<_, anyhow::Error>(()); // 2FA entered, logging in... | |
} | |
tokio::time::sleep(Duration::from_secs(1)).await; | |
} | |
}) | |
.await | |
.context("wait for one-time-code or login")??; | |
// Check if login successful or if 2FA required (Cancel button appears) | |
let loggedin = tokio::time::timeout(Duration::from_secs(20), async { | |
loop { | |
if has_new_email_button(webdriver) | |
.await | |
.context("search new-email button")? | |
{ | |
// Login successful | |
return Ok::<_, anyhow::Error>(true); | |
} | |
else if has_cancel_button(webdriver) | |
.await | |
.context("search cancel button")? | |
{ | |
// Cancel button present means 2FA needed | |
enter_onetimecode(&onetimecode, webdriver) | |
.await | |
.context("entering one-time-code and finishing login")?; | |
// 2FA entered, logging in... | |
return Ok::<_, anyhow::Error>(false); | |
} | |
tokio::time::sleep(Duration::from_secs(1)).await; | |
} | |
}) | |
.await | |
.context("wait for one-time-code or login")??; |
This is slightly more "Rusty".
``` | ||
|
||
Note: You can leave the `ONETIMECODE` variable as displayed above even if your account does not have 2FA, or you can remove that line from your `.env` file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the "make it optional" change I've proposed below, the user normally doesn't need it. In general I think editing the file for every single action is a bit cumbersome, so I would pass the code via CLI and instead of mentioning it here, add a subsection add the end of the "usage" section:
### 2FA
If you use 2 factor authentication (2FA), you need to pass the one-time-code to tatutanatata. This can either be done via a CLI parameter (`--onetimecode=000000`) or via a new entry within the `.env` file (`TUTANOTA_CLI_ONETIMECODE=000000`).
Technically there's even a third way: passing the env variable directly (i.e. env TUTANOTA_CLI_ONETIMECODE=000000 cargo run ...
) but techy users that know env variables probably also understand .env
files and the fact that the only reflect/set env variables.
Just checking in, will this still be merged into the main repo? :) |
I think merging this doesn't provide much at the moment before someone fixes #44, i.e. adjusts the code to the recent UI changes. |
I totally rewrote this tool to no longer use a webdriver but directly use the upstream protocol. 2FA would need a redesign to work with the new approach. |
Logs in, checks for 2FA window, inputs one-time-code; finishes login; See https://github.com/crepererum/tatutanatata/issues/10