Minor code refactors (#266)
This commit is contained in:
parent
7e94f76653
commit
11b36c566e
2 changed files with 176 additions and 163 deletions
171
src/login.rs
Normal file
171
src/login.rs
Normal file
|
@ -0,0 +1,171 @@
|
|||
use std::io::Write;
|
||||
|
||||
use log::*;
|
||||
use steamguard::{
|
||||
protobufs::steammessages_auth_steamclient::{EAuthSessionGuardType, EAuthTokenPlatformType},
|
||||
refresher::TokenRefresher,
|
||||
steamapi::{self, AuthenticationClient},
|
||||
token::Tokens,
|
||||
transport::Transport,
|
||||
DeviceDetails, LoginError, SteamGuardAccount, UserLogin,
|
||||
};
|
||||
|
||||
use crate::tui;
|
||||
|
||||
pub fn do_login<T: Transport + Clone>(
|
||||
transport: T,
|
||||
account: &mut SteamGuardAccount,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(tokens) = account.tokens.as_mut() {
|
||||
info!("Refreshing access token...");
|
||||
let client = AuthenticationClient::new(transport.clone());
|
||||
let mut refresher = TokenRefresher::new(client);
|
||||
match refresher.refresh(account.steam_id, tokens) {
|
||||
Ok(token) => {
|
||||
info!("Successfully refreshed access token, no need to prompt to log in.");
|
||||
tokens.set_access_token(token);
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"Failed to refresh access token, prompting for login: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !account.account_name.is_empty() {
|
||||
info!("Username: {}", account.account_name);
|
||||
} else {
|
||||
eprint!("Username: ");
|
||||
account.account_name = tui::prompt();
|
||||
}
|
||||
let _ = std::io::stdout().flush();
|
||||
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
|
||||
if !password.is_empty() {
|
||||
debug!("password is present");
|
||||
} else {
|
||||
debug!("password is empty");
|
||||
}
|
||||
let tokens = do_login_impl(
|
||||
transport,
|
||||
account.account_name.clone(),
|
||||
password,
|
||||
Some(account),
|
||||
)?;
|
||||
let steam_id = tokens.access_token().decode()?.steam_id();
|
||||
account.set_tokens(tokens);
|
||||
account.steam_id = steam_id;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_login_raw<T: Transport + Clone>(
|
||||
transport: T,
|
||||
username: String,
|
||||
) -> anyhow::Result<Tokens> {
|
||||
let _ = std::io::stdout().flush();
|
||||
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
|
||||
if !password.is_empty() {
|
||||
debug!("password is present");
|
||||
} else {
|
||||
debug!("password is empty");
|
||||
}
|
||||
do_login_impl(transport, username, password, None)
|
||||
}
|
||||
|
||||
fn do_login_impl<T: Transport + Clone>(
|
||||
transport: T,
|
||||
username: String,
|
||||
password: String,
|
||||
account: Option<&SteamGuardAccount>,
|
||||
) -> anyhow::Result<Tokens> {
|
||||
let mut login = UserLogin::new(transport.clone(), build_device_details());
|
||||
|
||||
let mut password = password;
|
||||
let confirmation_methods;
|
||||
loop {
|
||||
match login.begin_auth_via_credentials(&username, &password) {
|
||||
Ok(methods) => {
|
||||
confirmation_methods = methods;
|
||||
break;
|
||||
}
|
||||
Err(LoginError::TooManyAttempts) => {
|
||||
error!("Too many login attempts. Steam is rate limiting you. Please wait a while and try again later.");
|
||||
return Err(LoginError::TooManyAttempts.into());
|
||||
}
|
||||
Err(LoginError::BadCredentials) => {
|
||||
error!("Incorrect password.");
|
||||
password = rpassword::prompt_password_stdout("Password: ")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.to_owned();
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Unexpected error when trying to log in. If you report this as a bug, please rerun with `-v debug` or `-v trace` and include all output in your issue. {:?}", err);
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for method in confirmation_methods {
|
||||
match method.confirmation_type {
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_DeviceConfirmation => {
|
||||
eprintln!("Please confirm this login on your other device.");
|
||||
eprintln!("Press enter when you have confirmed.");
|
||||
tui::pause();
|
||||
}
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_EmailConfirmation => {
|
||||
eprint!("Please confirm this login by clicking the link in your email.");
|
||||
if !method.associated_messsage.is_empty() {
|
||||
eprint!(" ({})", method.associated_messsage);
|
||||
}
|
||||
eprintln!();
|
||||
eprintln!("Press enter when you have confirmed.");
|
||||
tui::pause();
|
||||
}
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_DeviceCode => {
|
||||
let code = if let Some(account) = account {
|
||||
debug!("Generating 2fa code...");
|
||||
let time = steamapi::get_server_time(transport)?.server_time();
|
||||
account.generate_code(time)
|
||||
} else {
|
||||
eprint!("Enter the 2fa code from your device: ");
|
||||
tui::prompt().trim().to_owned()
|
||||
};
|
||||
login.submit_steam_guard_code(method.confirmation_type, code)?;
|
||||
}
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_EmailCode => {
|
||||
eprint!("Enter the 2fa code sent to your email: ");
|
||||
let code = tui::prompt().trim().to_owned();
|
||||
login.submit_steam_guard_code(method.confirmation_type, code)?;
|
||||
}
|
||||
_ => {
|
||||
warn!("Unknown confirmation method: {:?}", method);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
info!("Polling for tokens... -- If this takes a long time, try logging in again.");
|
||||
let tokens = login.poll_until_tokens()?;
|
||||
|
||||
info!("Logged in successfully!");
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
fn build_device_details() -> DeviceDetails {
|
||||
DeviceDetails {
|
||||
friendly_name: format!(
|
||||
"{} (steamguard-cli)",
|
||||
gethostname::gethostname()
|
||||
.into_string()
|
||||
.expect("failed to get hostname")
|
||||
),
|
||||
platform_type: EAuthTokenPlatformType::k_EAuthTokenPlatformType_MobileApp,
|
||||
os_type: -500,
|
||||
gaming_device_type: 528,
|
||||
}
|
||||
}
|
168
src/main.rs
168
src/main.rs
|
@ -3,21 +3,16 @@ use clap::Parser;
|
|||
use log::*;
|
||||
use secrecy::SecretString;
|
||||
use std::{
|
||||
io::Write,
|
||||
path::Path,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use steamguard::{
|
||||
protobufs::steammessages_auth_steamclient::{EAuthSessionGuardType, EAuthTokenPlatformType},
|
||||
refresher::TokenRefresher,
|
||||
transport::{Transport, WebApiTransport},
|
||||
};
|
||||
use steamguard::{steamapi, DeviceDetails, LoginError, SteamGuardAccount, UserLogin};
|
||||
use steamguard::{steamapi::AuthenticationClient, token::Tokens};
|
||||
use steamguard::transport::WebApiTransport;
|
||||
use steamguard::SteamGuardAccount;
|
||||
|
||||
use crate::accountmanager::migrate::{load_and_migrate, MigrationError};
|
||||
pub use crate::accountmanager::{AccountManager, ManifestAccountLoadError, ManifestLoadError};
|
||||
use crate::commands::{CommandType, Subcommands};
|
||||
pub use login::*;
|
||||
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
|
@ -32,8 +27,10 @@ mod commands;
|
|||
mod debug;
|
||||
mod encryption;
|
||||
mod errors;
|
||||
mod login;
|
||||
mod secret_string;
|
||||
pub(crate) mod tui;
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
mod updater;
|
||||
|
||||
|
@ -304,161 +301,6 @@ fn get_selected_accounts(
|
|||
Ok(selected_accounts)
|
||||
}
|
||||
|
||||
fn do_login<T: Transport + Clone>(
|
||||
transport: T,
|
||||
account: &mut SteamGuardAccount,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(tokens) = account.tokens.as_mut() {
|
||||
info!("Refreshing access token...");
|
||||
let client = AuthenticationClient::new(transport.clone());
|
||||
let mut refresher = TokenRefresher::new(client);
|
||||
match refresher.refresh(account.steam_id, tokens) {
|
||||
Ok(token) => {
|
||||
info!("Successfully refreshed access token, no need to prompt to log in.");
|
||||
tokens.set_access_token(token);
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"Failed to refresh access token, prompting for login: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !account.account_name.is_empty() {
|
||||
info!("Username: {}", account.account_name);
|
||||
} else {
|
||||
eprint!("Username: ");
|
||||
account.account_name = tui::prompt();
|
||||
}
|
||||
let _ = std::io::stdout().flush();
|
||||
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
|
||||
if !password.is_empty() {
|
||||
debug!("password is present");
|
||||
} else {
|
||||
debug!("password is empty");
|
||||
}
|
||||
let tokens = do_login_impl(
|
||||
transport,
|
||||
account.account_name.clone(),
|
||||
password,
|
||||
Some(account),
|
||||
)?;
|
||||
let steam_id = tokens.access_token().decode()?.steam_id();
|
||||
account.set_tokens(tokens);
|
||||
account.steam_id = steam_id;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_login_raw<T: Transport + Clone>(transport: T, username: String) -> anyhow::Result<Tokens> {
|
||||
let _ = std::io::stdout().flush();
|
||||
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
|
||||
if !password.is_empty() {
|
||||
debug!("password is present");
|
||||
} else {
|
||||
debug!("password is empty");
|
||||
}
|
||||
do_login_impl(transport, username, password, None)
|
||||
}
|
||||
|
||||
fn do_login_impl<T: Transport + Clone>(
|
||||
transport: T,
|
||||
username: String,
|
||||
password: String,
|
||||
account: Option<&SteamGuardAccount>,
|
||||
) -> anyhow::Result<Tokens> {
|
||||
let mut login = UserLogin::new(transport.clone(), build_device_details());
|
||||
|
||||
let mut password = password;
|
||||
let confirmation_methods;
|
||||
loop {
|
||||
match login.begin_auth_via_credentials(&username, &password) {
|
||||
Ok(methods) => {
|
||||
confirmation_methods = methods;
|
||||
break;
|
||||
}
|
||||
Err(LoginError::TooManyAttempts) => {
|
||||
error!("Too many login attempts. Steam is rate limiting you. Please wait a while and try again later.");
|
||||
return Err(LoginError::TooManyAttempts.into());
|
||||
}
|
||||
Err(LoginError::BadCredentials) => {
|
||||
error!("Incorrect password.");
|
||||
password = rpassword::prompt_password_stdout("Password: ")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.to_owned();
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Unexpected error when trying to log in. If you report this as a bug, please rerun with `-v debug` or `-v trace` and include all output in your issue. {:?}", err);
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for method in confirmation_methods {
|
||||
match method.confirmation_type {
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_DeviceConfirmation => {
|
||||
eprintln!("Please confirm this login on your other device.");
|
||||
eprintln!("Press enter when you have confirmed.");
|
||||
tui::pause();
|
||||
}
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_EmailConfirmation => {
|
||||
eprint!("Please confirm this login by clicking the link in your email.");
|
||||
if !method.associated_messsage.is_empty() {
|
||||
eprint!(" ({})", method.associated_messsage);
|
||||
}
|
||||
eprintln!();
|
||||
eprintln!("Press enter when you have confirmed.");
|
||||
tui::pause();
|
||||
}
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_DeviceCode => {
|
||||
let code = if let Some(account) = account {
|
||||
debug!("Generating 2fa code...");
|
||||
let time = steamapi::get_server_time(transport)?.server_time();
|
||||
account.generate_code(time)
|
||||
} else {
|
||||
eprint!("Enter the 2fa code from your device: ");
|
||||
tui::prompt().trim().to_owned()
|
||||
};
|
||||
login.submit_steam_guard_code(method.confirmation_type, code)?;
|
||||
}
|
||||
EAuthSessionGuardType::k_EAuthSessionGuardType_EmailCode => {
|
||||
eprint!("Enter the 2fa code sent to your email: ");
|
||||
let code = tui::prompt().trim().to_owned();
|
||||
login.submit_steam_guard_code(method.confirmation_type, code)?;
|
||||
}
|
||||
_ => {
|
||||
warn!("Unknown confirmation method: {:?}", method);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
info!("Polling for tokens... -- If this takes a long time, try logging in again.");
|
||||
let tokens = login.poll_until_tokens()?;
|
||||
|
||||
info!("Logged in successfully!");
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
fn build_device_details() -> DeviceDetails {
|
||||
DeviceDetails {
|
||||
friendly_name: format!(
|
||||
"{} (steamguard-cli)",
|
||||
gethostname::gethostname()
|
||||
.into_string()
|
||||
.expect("failed to get hostname")
|
||||
),
|
||||
platform_type: EAuthTokenPlatformType::k_EAuthTokenPlatformType_MobileApp,
|
||||
os_type: -500,
|
||||
gaming_device_type: 528,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mafiles_dir() -> String {
|
||||
let paths = vec![
|
||||
Path::new(&dirs::config_dir().unwrap()).join("steamguard-cli/maFiles"),
|
||||
|
|
Loading…
Reference in a new issue