Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ CLAUDE.md
build-output/
*.apk
!android/app/build/outputs/apk/
.claude/
10 changes: 2 additions & 8 deletions native/rust-core/src/api/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1402,14 +1402,8 @@ pub async fn exchange_authorization_code(
"requested_extensions": ["device_info", "customer_info"]
});

// Log the request for debugging
eprintln!("=== Device Registration Request ===");
eprintln!("URL: {}", register_url);
eprintln!(
"Body: {}",
serde_json::to_string_pretty(&request_body).unwrap_or_default()
);
eprintln!("===================================");
// Never log the request body: it contains the authorization code and
// PKCE verifier, and stderr is readable via logcat on Android.

// Make HTTP request
let client = reqwest::Client::new();
Expand Down
19 changes: 8 additions & 11 deletions native/rust-core/src/api/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,17 +706,11 @@ impl AudibleClient {
match serde_json::from_str::<T>(&response_text) {
Ok(data) => Ok(data),
Err(e) => {
// Extract context around the error location (800 chars)
let error_col = e.column();
let start = error_col.saturating_sub(400);
let end = (error_col + 400).min(response_text.len());
let context = &response_text[start..end];

// Do not echo the response body into the error message; a
// partial API response can contain token or account data.
Err(LibationError::InvalidApiResponse {
message: format!(
"Parse error: {} at col {}. Context: ...{}...",
e, error_col, context
),
message: format!("Parse error: {} at line {} col {}", e, e.line(), e.column()),
// Body kept out of the message; this field is not displayed.
response_body: Some(response_text),
})
}
Expand All @@ -729,8 +723,11 @@ impl AudibleClient {
let url = response.url().clone();
let error_body = response.text().await.unwrap_or_default();

// Cap the reflected body: error responses can echo request data.
let snippet: String = error_body.chars().take(300).collect();

Err(LibationError::api_failed(
format!("API request failed: {}", error_body),
format!("API request failed: {}", snippet),
Some(status.as_u16()),
Some(self.extract_endpoint_from_url(url.as_str())),
))
Expand Down
15 changes: 3 additions & 12 deletions native/rust-core/src/api/license.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,24 +418,15 @@ impl KeyData {
LibationError::InvalidInput(format!("Decrypted license is not valid UTF-8: {}", e))
})?;

// Debug: print decrypted JSON
eprintln!("🔍 DEBUG: Decrypted voucher JSON:\n{}\n", json_str);
// Never log json_str: it contains the AES key/iv for the audiobook.

// Parse JSON to get Voucher
// Reference: ContentLicenseDtoV10.cs:46 - VoucherDtoV10.FromJson(plainText)
let voucher: Voucher = serde_json::from_str(&json_str).map_err(|e| {
LibationError::InvalidInput(format!(
"Failed to parse decrypted voucher JSON: {}\nJSON was: {}",
e, json_str
))
// Do not include json_str in the error: it contains the key/iv.
LibationError::InvalidInput(format!("Failed to parse decrypted voucher JSON: {}", e))
})?;

eprintln!(
"🔍 DEBUG: Voucher key length: {}, iv length: {:?}",
voucher.key.len(),
voucher.iv.as_ref().map(|s| s.len())
);

// Convert voucher to KeyData
// Check if key is hex (32 chars) or base64 (24 chars)
if voucher.key.len() == 32 {
Expand Down
28 changes: 14 additions & 14 deletions native/rust-core/src/api/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ mod tests {

// Verify basic structure
assert_eq!(response.request_id, "8e570a18-8232-4df0-a212-c0d21860fcd5");
assert_eq!(response.response.success.customer_id, "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA");
assert_eq!(response.response.success.customer_id, "amzn1.account.TESTFIXTUREACCOUNT00000000000");
}

#[test]
Expand Down Expand Up @@ -309,8 +309,8 @@ mod tests {
let response = RegistrationResponse::from_json(TEST_FIXTURE).unwrap();
let device_info = &response.response.success.extensions.device_info;

assert_eq!(device_info.device_name, "Henning's 7th Android");
assert_eq!(device_info.device_serial_number, "B45EF975C33A7B7E8DAF4D96E39B8040");
assert_eq!(device_info.device_name, "Test Fixture Android");
assert_eq!(device_info.device_serial_number, "00000000000000000000000000000000");
assert_eq!(device_info.device_type, "A10KISP2GWF0E4");
}

Expand All @@ -320,10 +320,10 @@ mod tests {
let customer_info = &response.response.success.extensions.customer_info;

assert_eq!(customer_info.account_pool, "Amazon");
assert_eq!(customer_info.user_id, "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA");
assert_eq!(customer_info.user_id, "amzn1.account.TESTFIXTUREACCOUNT00000000000");
assert_eq!(customer_info.home_region, "NA");
assert_eq!(customer_info.name, "Henning Berge");
assert_eq!(customer_info.given_name, "Henning");
assert_eq!(customer_info.name, "Test User");
assert_eq!(customer_info.given_name, "Test");
}

#[test]
Expand All @@ -344,13 +344,13 @@ mod tests {
assert!(data.adp_token.contains("{enc:"));

// Verify device info
assert_eq!(data.device_serial_number, "B45EF975C33A7B7E8DAF4D96E39B8040");
assert_eq!(data.device_serial_number, "00000000000000000000000000000000");
assert_eq!(data.device_type, "A10KISP2GWF0E4");
assert_eq!(data.device_name, "Henning's 7th Android");
assert_eq!(data.device_name, "Test Fixture Android");

// Verify customer info
assert_eq!(data.amazon_account_id, "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA");
assert_eq!(data.customer_info.name, "Henning Berge");
assert_eq!(data.amazon_account_id, "amzn1.account.TESTFIXTUREACCOUNT00000000000");
assert_eq!(data.customer_info.name, "Test User");
assert_eq!(data.customer_info.home_region, "NA");

// Verify cookies
Expand All @@ -370,11 +370,11 @@ mod tests {
// Verify Identity fields
assert!(identity.access_token.token.starts_with("Atna|"));
assert!(identity.refresh_token.starts_with("Atnr|"));
assert_eq!(identity.device_serial_number, "B45EF975C33A7B7E8DAF4D96E39B8040");
assert_eq!(identity.device_serial_number, "00000000000000000000000000000000");
assert_eq!(identity.device_type, "A10KISP2GWF0E4");
assert_eq!(identity.amazon_account_id, "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA");
assert_eq!(identity.amazon_account_id, "amzn1.account.TESTFIXTUREACCOUNT00000000000");
assert_eq!(identity.locale, locale);
assert_eq!(identity.customer_info.name, "Henning Berge");
assert_eq!(identity.customer_info.name, "Test User");
}

#[test]
Expand Down Expand Up @@ -434,6 +434,6 @@ mod tests {

// These should be the same
assert_eq!(customer_id, user_id);
assert_eq!(customer_id, "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA");
assert_eq!(customer_id, "amzn1.account.TESTFIXTUREACCOUNT00000000000");
}
}
14 changes: 7 additions & 7 deletions native/rust-core/test_fixtures/registration_response.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"request_id": "8e570a18-8232-4df0-a212-c0d21860fcd5",
"response": {
"success": {
"customer_id": "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA",
"customer_id": "amzn1.account.TESTFIXTUREACCOUNT00000000000",
"tokens": {
"bearer": {
"access_token": "Atna|synthetic-access-token-not-valid-for-api-use-000000000000000000000000000000000000000000000000000000000000",
Expand All @@ -11,7 +11,7 @@
},
"mac_dms": {
"device_private_key": "MIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"adp_token": "{enc:synthetic-encrypted-payload}{key:synthetic-key}{iv:synthetic-iv}{name:Henning's 7th Android}{serial:B45EF975C33A7B7E8DAF4D96E39B8040}"
"adp_token": "{enc:synthetic-encrypted-payload}{key:synthetic-key}{iv:synthetic-iv}{name:Test Fixture Android}{serial:00000000000000000000000000000000}"
},
"website_cookies": [
{
Expand Down Expand Up @@ -67,16 +67,16 @@
},
"extensions": {
"device_info": {
"device_name": "Henning's 7th Android",
"device_serial_number": "B45EF975C33A7B7E8DAF4D96E39B8040",
"device_name": "Test Fixture Android",
"device_serial_number": "00000000000000000000000000000000",
"device_type": "A10KISP2GWF0E4"
},
"customer_info": {
"account_pool": "Amazon",
"user_id": "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA",
"user_id": "amzn1.account.TESTFIXTUREACCOUNT00000000000",
"home_region": "NA",
"name": "Henning Berge",
"given_name": "Henning"
"name": "Test User",
"given_name": "Test"
}
}
}
Expand Down
22 changes: 11 additions & 11 deletions native/rust-core/tests/integration_oauth_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn test_parse_real_registration_response() {
assert_eq!(response.request_id, "8e570a18-8232-4df0-a212-c0d21860fcd5");
assert_eq!(
response.response.success.customer_id,
"amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA"
"amzn1.account.TESTFIXTUREACCOUNT00000000000"
);

println!("✅ Registration response parsed successfully");
Expand All @@ -51,13 +51,13 @@ fn test_extract_all_tokens() {
assert!(data.adp_token.contains("{key:"));

// Device info
assert_eq!(data.device_serial_number, "B45EF975C33A7B7E8DAF4D96E39B8040");
assert_eq!(data.device_serial_number, "00000000000000000000000000000000");
assert_eq!(data.device_type, "A10KISP2GWF0E4");
assert_eq!(data.device_name, "Henning's 7th Android");
assert_eq!(data.device_name, "Test Fixture Android");

// Customer info
assert_eq!(data.customer_info.name, "Henning Berge");
assert_eq!(data.customer_info.given_name, "Henning");
assert_eq!(data.customer_info.name, "Test User");
assert_eq!(data.customer_info.given_name, "Test");
assert_eq!(data.customer_info.home_region, "NA");

// Cookies
Expand All @@ -84,9 +84,9 @@ fn test_create_identity_from_real_data() {
// Verify Identity has all required fields
assert!(identity.access_token.token.len() > 50);
assert!(identity.refresh_token.len() > 50);
assert_eq!(identity.device_serial_number, "B45EF975C33A7B7E8DAF4D96E39B8040");
assert_eq!(identity.device_serial_number, "00000000000000000000000000000000");
assert_eq!(identity.device_type, "A10KISP2GWF0E4");
assert_eq!(identity.amazon_account_id, "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA");
assert_eq!(identity.amazon_account_id, "amzn1.account.TESTFIXTUREACCOUNT00000000000");
assert_eq!(identity.locale, locale);

// Verify not expired
Expand Down Expand Up @@ -117,7 +117,7 @@ fn test_create_account_with_real_identity() {

// Verify account
assert_eq!(account.account_id, account_id);
assert_eq!(account.account_name, "Henning Berge");
assert_eq!(account.account_name, "Test User");
assert!(account.identity.is_some());
assert!(!account.needs_token_refresh());

Expand Down Expand Up @@ -222,10 +222,10 @@ fn test_customer_info_complete() {

// Verify all fields
assert_eq!(customer_info.account_pool, "Amazon");
assert_eq!(customer_info.user_id, "amzn1.account.AGMGLSGIFYVALF2MEO4F3JJQRLSA");
assert_eq!(customer_info.user_id, "amzn1.account.TESTFIXTUREACCOUNT00000000000");
assert_eq!(customer_info.home_region, "NA");
assert_eq!(customer_info.name, "Henning Berge");
assert_eq!(customer_info.given_name, "Henning");
assert_eq!(customer_info.name, "Test User");
assert_eq!(customer_info.given_name, "Test");

// Verify customer_id matches user_id
assert_eq!(
Expand Down
Loading