Add comments, reduce unnceccsary headers, better sms error handling

This commit is contained in:
nab138
2026-01-26 14:48:59 -05:00
parent 2167300f98
commit 846b6cde05
4 changed files with 91 additions and 46 deletions

View File

@@ -19,7 +19,7 @@ pub struct AnisetteClientInfo {
pub struct AnisetteData { pub struct AnisetteData {
machine_id: String, machine_id: String,
one_time_password: String, one_time_password: String,
routing_info: String, pub routing_info: String,
device_description: String, device_description: String,
device_unique_identifier: String, device_unique_identifier: String,
local_user_id: String, local_user_id: String,
@@ -27,29 +27,29 @@ pub struct AnisetteData {
impl AnisetteData { impl AnisetteData {
pub fn get_headers(&self, serial: String) -> HashMap<String, String> { pub fn get_headers(&self, serial: String) -> HashMap<String, String> {
let dt: DateTime<Utc> = Utc::now().round_subsecs(0); // let dt: DateTime<Utc> = Utc::now().round_subsecs(0);
HashMap::from_iter( HashMap::from_iter(
[ [
( // (
"X-Apple-I-Client-Time".to_string(), // "X-Apple-I-Client-Time".to_string(),
dt.format("%+").to_string().replace("+00:00", "Z"), // dt.format("%+").to_string().replace("+00:00", "Z"),
), // ),
("X-Apple-I-SRL-NO".to_string(), serial), // ("X-Apple-I-SRL-NO".to_string(), serial),
("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), // ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()),
("X-Apple-Locale".to_string(), "en_US".to_string()), // ("X-Apple-Locale".to_string(), "en_US".to_string()),
("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), // ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()),
("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), // ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()),
( (
"X-Mme-Device-Id".to_string(), "X-Mme-Device-Id".to_string(),
self.device_unique_identifier.clone(), self.device_unique_identifier.clone(),
), ),
("X-Apple-I-MD".to_string(), self.one_time_password.clone()), ("X-Apple-I-MD".to_string(), self.one_time_password.clone()),
("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()),
( // (
"X-Mme-Client-Info".to_string(), // "X-Mme-Client-Info".to_string(),
self.device_description.clone(), // self.device_description.clone(),
), // ),
] ]
.into_iter(), .into_iter(),
) )

View File

@@ -175,18 +175,18 @@ impl RemoteV3AnisetteProvider {
"X-Apple-I-MD-LU", "X-Apple-I-MD-LU",
HeaderValue::from_str(&hex::encode(state.get_md_lu()))?, HeaderValue::from_str(&hex::encode(state.get_md_lu()))?,
); );
headers.insert( // headers.insert(
"X-Apple-I-Client-Time", // "X-Apple-I-Client-Time",
HeaderValue::from_str( // HeaderValue::from_str(
&Utc::now() // &Utc::now()
.round_subsecs(0) // .round_subsecs(0)
.format("%+") // .format("%+")
.to_string() // .to_string()
.replace("+00:00", "Z"), // .replace("+00:00", "Z"),
)?, // )?,
); // );
headers.insert("X-Apple-I-TimeZone", HeaderValue::from_static("UTC")); // headers.insert("X-Apple-I-TimeZone", HeaderValue::from_static("UTC"));
headers.insert("X-Apple-Locale", HeaderValue::from_static("en_US")); // headers.insert("X-Apple-Locale", HeaderValue::from_static("en_US"));
headers.insert( headers.insert(
"X-Mme-Device-Id", "X-Mme-Device-Id",
HeaderValue::from_str(&state.get_device_id())?, HeaderValue::from_str(&state.get_device_id())?,

View File

@@ -72,7 +72,7 @@ impl AppleAccountBuilder {
self self
} }
/// Build the AppleAccount /// Build the AppleAccount without logging in
/// ///
/// # Errors /// # Errors
/// Returns an error if the reqwest client cannot be built /// Returns an error if the reqwest client cannot be built
@@ -118,6 +118,10 @@ impl AppleAccount {
/// Build the apple account with the given email /// Build the apple account with the given email
/// ///
/// Reccomended to use the AppleAccountBuilder instead /// Reccomended to use the AppleAccountBuilder instead
/// # Arguments
/// - `email`: The Apple ID email address
/// - `anisette_provider`: The anisette provider to use
/// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection
pub async fn new( pub async fn new(
email: &str, email: &str,
mut anisette_provider: Box<dyn AnisetteProvider>, mut anisette_provider: Box<dyn AnisetteProvider>,
@@ -151,6 +155,12 @@ impl AppleAccount {
}) })
} }
/// Log in to the Apple ID account
/// # Arguments
/// - `password`: The Apple ID password
/// - `two_factor_callback`: A callback function that returns the two-factor authentication code
/// # Errors
/// Returns an error if the login fails
pub async fn login( pub async fn login(
&mut self, &mut self,
password: &str, password: &str,
@@ -216,6 +226,7 @@ impl AppleAccount {
} }
} }
/// Get the user's first and last name associated with the Apple ID
pub fn get_name(&self) -> Result<(String, String), Report> { pub fn get_name(&self) -> Result<(String, String), Report> {
let spd = self let spd = self
.spd .spd
@@ -269,6 +280,10 @@ impl AppleAccount {
.grandslam_client .grandslam_client
.get(&submit_code_url)? .get(&submit_code_url)?
.headers(self.build_2fa_headers().await?) .headers(self.build_2fa_headers().await?)
.header(
"X-Apple-I-MD-RINFO",
self.anisette_data.routing_info.clone(),
)
.header("security-code", code) .header("security-code", code)
.send() .send()
.await .await
@@ -321,8 +336,6 @@ impl AppleAccount {
"mode": "sms" "mode": "sms"
}); });
debug!("{}", body);
let mut headers = self.build_2fa_headers().await?; let mut headers = self.build_2fa_headers().await?;
headers.insert("Content-Type", HeaderValue::from_static("application/json")); headers.insert("Content-Type", HeaderValue::from_static("application/json"));
headers.insert( headers.insert(
@@ -337,14 +350,46 @@ impl AppleAccount {
.body(body.to_string()) .body(body.to_string())
.send() .send()
.await .await
.context("Failed to submit SMS 2FA code")? .context("Failed to submit SMS 2FA code")?;
.error_for_status()
.context("SMS 2FA code submission failed")? if !res.status().is_success() {
let status = res.status();
let text = res
.text() .text()
.await .await
.context("Failed to read SMS 2FA response")?; .context("Failed to read SMS 2FA error response text")?;
// try to parse as json, if it fails, just bail with the text
debug!("SMS 2FA response: {}", res); if let Ok(json) = serde_json::from_str::<serde_json::Value>(&text) {
if let Some(service_errors) = json.get("serviceErrors") {
if let Some(first_error) = service_errors.as_array().and_then(|arr| arr.get(0))
{
let code = first_error
.get("code")
.and_then(|c| c.as_str())
.unwrap_or("unknown");
let title = first_error
.get("title")
.and_then(|t| t.as_str())
.unwrap_or("No title provided");
let message = first_error
.get("message")
.and_then(|m| m.as_str())
.unwrap_or("No message provided");
bail!(
"SMS 2FA code submission failed (code {}): {} - {}",
code,
title,
message
);
}
}
}
bail!(
"SMS 2FA code submission failed with http status {}: {}",
status,
text
);
};
Ok(()) Ok(())
} }

View File

@@ -133,15 +133,15 @@ impl GrandSlam {
"X-Mme-Client-Info", "X-Mme-Client-Info",
HeaderValue::from_str(&self.client_info.client_info)?, HeaderValue::from_str(&self.client_info.client_info)?,
); );
headers.insert( // headers.insert(
"User-Agent", // "User-Agent",
HeaderValue::from_str(&self.client_info.user_agent)?, // HeaderValue::from_str(&self.client_info.user_agent)?,
); // );
headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); // headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)"));
headers.insert( // headers.insert(
"X-Apple-App-Info", // "X-Apple-App-Info",
HeaderValue::from_static("com.apple.gs.xcode.auth"), // HeaderValue::from_static("com.apple.gs.xcode.auth"),
); // );
Ok(headers) Ok(headers)
} }