mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 06:26:16 +01:00
Add SMS 2FA
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::{env, path::PathBuf};
|
||||
use std::env;
|
||||
|
||||
use isideload::{
|
||||
anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder,
|
||||
@@ -10,19 +10,19 @@ use tracing_subscriber::FmtSubscriber;
|
||||
async fn main() {
|
||||
isideload::init().expect("Failed to initialize error reporting");
|
||||
let subscriber = FmtSubscriber::builder()
|
||||
.with_max_level(Level::INFO)
|
||||
.with_max_level(Level::DEBUG)
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let _app_path = PathBuf::from(
|
||||
args.get(1)
|
||||
.expect("Please provide the path to the app to install"),
|
||||
);
|
||||
// let _app_path = PathBuf::from(
|
||||
// args.get(1)
|
||||
// .expect("Please provide the path to the app to install"),
|
||||
// );
|
||||
let apple_id = args
|
||||
.get(2)
|
||||
.get(1)
|
||||
.expect("Please provide the Apple ID to use for installation");
|
||||
let apple_password = args.get(3).expect("Please provide the Apple ID password");
|
||||
let apple_password = args.get(2).expect("Please provide the Apple ID password");
|
||||
|
||||
let get_2fa_code = || {
|
||||
let mut code = String::new();
|
||||
|
||||
@@ -199,7 +199,7 @@ impl RemoteV3AnisetteProvider {
|
||||
gs: &mut GrandSlam,
|
||||
url: &str,
|
||||
) -> Result<(), Report> {
|
||||
info!("Starting provisioning");
|
||||
debug!("Starting provisioning");
|
||||
|
||||
let start_provisioning = gs.get_url("midStartProvisioning").await?;
|
||||
let end_provisioning = gs.get_url("midFinishProvisioning").await?;
|
||||
|
||||
@@ -156,7 +156,7 @@ impl AppleAccount {
|
||||
password: &str,
|
||||
two_factor_callback: impl Fn() -> Option<String>,
|
||||
) -> Result<(), Report> {
|
||||
info!("Logging in to apple ID: {}", self.email);
|
||||
info!("Logging in to Apple ID: {}", self.email);
|
||||
if self.debug {
|
||||
warn!("Debug mode enabled: this is a security risk!");
|
||||
}
|
||||
@@ -184,6 +184,36 @@ impl AppleAccount {
|
||||
return Ok(());
|
||||
}
|
||||
LoginState::NeedsDevice2FA => {
|
||||
self.trusted_device_2fa(&two_factor_callback)
|
||||
.await
|
||||
.context("Failed to complete trusted device 2FA")?;
|
||||
debug!("Trusted device 2FA completed, need to login again");
|
||||
self.login_state = LoginState::NeedsLogin;
|
||||
}
|
||||
LoginState::NeedsSMS2FA => {
|
||||
info!("SMS 2FA required");
|
||||
self.sms_2fa(&two_factor_callback)
|
||||
.await
|
||||
.context("Failed to complete SMS 2FA")?;
|
||||
debug!("SMS 2FA completed, need to login again");
|
||||
self.login_state = LoginState::NeedsLogin;
|
||||
}
|
||||
LoginState::NeedsExtraStep(_) => todo!(),
|
||||
LoginState::NeedsLogin => {
|
||||
debug!("Logging in again...");
|
||||
self.login_state = self
|
||||
.login_inner(password)
|
||||
.await
|
||||
.context("Failed to login again")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn trusted_device_2fa(
|
||||
&mut self,
|
||||
two_factor_callback: impl Fn() -> Option<String>,
|
||||
) -> Result<(), Report> {
|
||||
debug!("Trusted device 2FA required");
|
||||
let request_code_url = self
|
||||
.grandslam_client
|
||||
@@ -203,8 +233,8 @@ impl AppleAccount {
|
||||
|
||||
info!("Trusted device 2FA request sent");
|
||||
|
||||
let code = two_factor_callback()
|
||||
.ok_or_else(|| report!("No 2FA code provided, aborting"))?;
|
||||
let code =
|
||||
two_factor_callback().ok_or_else(|| report!("No 2FA code provided, aborting"))?;
|
||||
|
||||
let res = self
|
||||
.grandslam_client
|
||||
@@ -227,23 +257,59 @@ impl AppleAccount {
|
||||
.check_grandslam_error()
|
||||
.context("Trusted device 2FA rejected")?;
|
||||
|
||||
debug!("Trusted device 2FA completed, need to login again");
|
||||
self.login_state = LoginState::NeedsLogin;
|
||||
Ok(())
|
||||
}
|
||||
LoginState::NeedsSMS2FA => {
|
||||
info!("SMS 2FA required");
|
||||
todo!();
|
||||
}
|
||||
LoginState::NeedsExtraStep(_) => todo!(),
|
||||
LoginState::NeedsLogin => {
|
||||
debug!("Logging in again...");
|
||||
self.login_state = self
|
||||
.login_inner(password)
|
||||
|
||||
async fn sms_2fa(
|
||||
&mut self,
|
||||
two_factor_callback: impl Fn() -> Option<String>,
|
||||
) -> Result<(), Report> {
|
||||
debug!("SMS 2FA required");
|
||||
let request_code_url = self.grandslam_client.get_url("secondaryAuth").await?;
|
||||
|
||||
self.grandslam_client
|
||||
.get_sms(&request_code_url)?
|
||||
.headers(self.build_2fa_headers().await?)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to login again")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
.context("Failed to request SMS 2fa")?
|
||||
.error_for_status()
|
||||
.context("SMS 2FA request failed")?;
|
||||
|
||||
info!("SMS 2FA request sent");
|
||||
|
||||
let code =
|
||||
two_factor_callback().ok_or_else(|| report!("No 2FA code provided, aborting"))?;
|
||||
|
||||
let body = serde_json::json!({
|
||||
"securityCode": {
|
||||
"code": code
|
||||
},
|
||||
"phoneNumber": {
|
||||
"id": "1"
|
||||
},
|
||||
"mode": "sms"
|
||||
});
|
||||
|
||||
let res = self
|
||||
.grandslam_client
|
||||
.post("https://gsa.apple.com/auth/verify/phone/securitycode")?
|
||||
.headers(self.build_2fa_headers().await?)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.body(body.to_string())
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to submit SMS 2fa code")?
|
||||
.error_for_status()
|
||||
.context("SMS 2FA code submission failed")?
|
||||
.text()
|
||||
.await
|
||||
.context("Failed to read SMS 2FA response text")?;
|
||||
|
||||
debug!("SMS 2FA response: {}", res);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn build_2fa_headers(&mut self) -> Result<HeaderMap, Report> {
|
||||
@@ -407,6 +473,7 @@ impl AppleAccount {
|
||||
return Ok(match s.as_str() {
|
||||
"trustedDeviceSecondaryAuth" => LoginState::NeedsDevice2FA,
|
||||
"secondaryAuth" => LoginState::NeedsSMS2FA,
|
||||
"repair" => LoginState::LoggedIn, // Just means that you don't have 2FA set up
|
||||
unknown => LoginState::NeedsExtraStep(unknown.to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ impl GrandSlam {
|
||||
let resp = self
|
||||
.client
|
||||
.get(URL_BAG)
|
||||
.headers(self.base_headers()?)
|
||||
.headers(self.base_headers(false)?)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to fetch URL Bag")?
|
||||
@@ -71,13 +71,19 @@ impl GrandSlam {
|
||||
}
|
||||
|
||||
pub fn get(&self, url: &str) -> Result<reqwest::RequestBuilder, Report> {
|
||||
let builder = self.client.get(url).headers(self.base_headers()?);
|
||||
let builder = self.client.get(url).headers(self.base_headers(false)?);
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
pub fn get_sms(&self, url: &str) -> Result<reqwest::RequestBuilder, Report> {
|
||||
let builder = self.client.get(url).headers(self.base_headers(true)?);
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
pub fn post(&self, url: &str) -> Result<reqwest::RequestBuilder, Report> {
|
||||
let builder = self.client.post(url).headers(self.base_headers()?);
|
||||
let builder = self.client.post(url).headers(self.base_headers(false)?);
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
@@ -117,10 +123,12 @@ impl GrandSlam {
|
||||
Ok(response_plist)
|
||||
}
|
||||
|
||||
fn base_headers(&self) -> Result<reqwest::header::HeaderMap, Report> {
|
||||
fn base_headers(&self, sms: bool) -> Result<reqwest::header::HeaderMap, Report> {
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
if !sms {
|
||||
headers.insert("Content-Type", HeaderValue::from_static("text/x-xml-plist"));
|
||||
headers.insert("Accept", HeaderValue::from_static("text/x-xml-plist"));
|
||||
}
|
||||
headers.insert(
|
||||
"X-Mme-Client-Info",
|
||||
HeaderValue::from_str(&self.client_info.client_info)?,
|
||||
|
||||
Reference in New Issue
Block a user