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 log::*;
|
||||||
use secrecy::SecretString;
|
use secrecy::SecretString;
|
||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
use steamguard::{
|
use steamguard::transport::WebApiTransport;
|
||||||
protobufs::steammessages_auth_steamclient::{EAuthSessionGuardType, EAuthTokenPlatformType},
|
use steamguard::SteamGuardAccount;
|
||||||
refresher::TokenRefresher,
|
|
||||||
transport::{Transport, WebApiTransport},
|
|
||||||
};
|
|
||||||
use steamguard::{steamapi, DeviceDetails, LoginError, SteamGuardAccount, UserLogin};
|
|
||||||
use steamguard::{steamapi::AuthenticationClient, token::Tokens};
|
|
||||||
|
|
||||||
use crate::accountmanager::migrate::{load_and_migrate, MigrationError};
|
use crate::accountmanager::migrate::{load_and_migrate, MigrationError};
|
||||||
pub use crate::accountmanager::{AccountManager, ManifestAccountLoadError, ManifestLoadError};
|
pub use crate::accountmanager::{AccountManager, ManifestAccountLoadError, ManifestLoadError};
|
||||||
use crate::commands::{CommandType, Subcommands};
|
use crate::commands::{CommandType, Subcommands};
|
||||||
|
pub use login::*;
|
||||||
|
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -32,8 +27,10 @@ mod commands;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod encryption;
|
mod encryption;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod login;
|
||||||
mod secret_string;
|
mod secret_string;
|
||||||
pub(crate) mod tui;
|
pub(crate) mod tui;
|
||||||
|
|
||||||
#[cfg(feature = "updater")]
|
#[cfg(feature = "updater")]
|
||||||
mod updater;
|
mod updater;
|
||||||
|
|
||||||
|
@ -304,161 +301,6 @@ fn get_selected_accounts(
|
||||||
Ok(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 {
|
fn get_mafiles_dir() -> String {
|
||||||
let paths = vec![
|
let paths = vec![
|
||||||
Path::new(&dirs::config_dir().unwrap()).join("steamguard-cli/maFiles"),
|
Path::new(&dirs::config_dir().unwrap()).join("steamguard-cli/maFiles"),
|
||||||
|
|
Loading…
Reference in a new issue