From 2eb8e464e811b82523ca35e8626cd3cbc0b47df3 Mon Sep 17 00:00:00 2001 From: Akshat Pasbola Date: Fri, 16 Oct 2026 10:35:18 +0100 Subject: [PATCH] gsa: fix login after Android client-info switch Since #337, the GSA URL bag lookup sends the Android client info, so Apple returns the reduced Android bag pointing at gsd.apple.com instead of gsa.apple.com (see #339). Matches isideload 0.4.5: - Use the Mac client info only for the URL bag lookup - Use the Android client info and akd user agent for all other GSA requests, including ADI provisioning - Stop sending Mac hardware identifiers in cpd --- .../GrandSlam/Anisette/ADIDataProvider.swift | 6 ++---- .../XKit/GrandSlam/Anisette/DeviceInfo.swift | 18 ++++++++++++++---- .../Anisette/OmnisetteADIProvider.swift | 11 ----------- .../XKit/GrandSlam/Anisette/XADIProvider.swift | 6 ------ .../Lookup/GrandSlamLookupManager.swift | 2 +- .../Requests/GrandSlamOperationRequest.swift | 3 ++- Sources/XToolSupport/AuthCommand.swift | 2 +- 7 files changed, 20 insertions(+), 28 deletions(-) diff --git a/Sources/XKit/GrandSlam/Anisette/ADIDataProvider.swift b/Sources/XKit/GrandSlam/Anisette/ADIDataProvider.swift index 2290f1f5..4bb36337 100644 --- a/Sources/XKit/GrandSlam/Anisette/ADIDataProvider.swift +++ b/Sources/XKit/GrandSlam/Anisette/ADIDataProvider.swift @@ -20,10 +20,8 @@ public protocol RawADIProvider: Sendable { extension RawADIProvider { public func clientInfo() async throws -> String { - // Looks like Apple detects attempts to use a macOS client string on Windows :/ - """ - - """ + // Must match the client info used for GSA auth, since the ADI data is Android-generated. + DeviceInfo.ClientInfo.androidClientString } } diff --git a/Sources/XKit/GrandSlam/Anisette/DeviceInfo.swift b/Sources/XKit/GrandSlam/Anisette/DeviceInfo.swift index 42c1d7f4..3ba5242f 100644 --- a/Sources/XKit/GrandSlam/Anisette/DeviceInfo.swift +++ b/Sources/XKit/GrandSlam/Anisette/DeviceInfo.swift @@ -19,14 +19,24 @@ public struct DeviceInfo: Codable, Sendable { public struct ClientInfo: Codable { public let modelID: String // e.g. MacBookPro11,5 + /// Our Anisette data comes from Android ADI, so every GSA request (including + /// provisioning) has to identify as an Android device or GSA rejects it (-80045). + static let androidClientString = """ + + """ + + /// Only used for the GSA URL bag lookup: sending the Android client info there + /// returns a reduced bag pointing at gsd.apple.com, which doesn't work for us. + static let urlBagClientString = """ + + """ + var clientString: String { - """ - - """ + Self.androidClientString } var userAgent: String { - "AuthKit/1 (Macintosh; OS X 27.0) (com.apple.dt.Xcode/26.5)" + "akd/1.0 CFNetwork/808.1.4" } public init(modelID: String) { diff --git a/Sources/XKit/GrandSlam/Anisette/OmnisetteADIProvider.swift b/Sources/XKit/GrandSlam/Anisette/OmnisetteADIProvider.swift index 63eaefba..f1f9a325 100644 --- a/Sources/XKit/GrandSlam/Anisette/OmnisetteADIProvider.swift +++ b/Sources/XKit/GrandSlam/Anisette/OmnisetteADIProvider.swift @@ -28,17 +28,6 @@ struct OmnisetteADIProvider: RawADIProvider { return encoder }() - func clientInfo() async throws -> String { - struct ClientInfo: Decodable { - let clientInfo: String - } - let body = try await client.makeRequest( - HTTPRequest(url: url.appendingPathComponent("v3/client_info")) - ).body - let clientInfo = try Self.decoder.decode(ClientInfo.self, from: body) - return clientInfo.clientInfo - } - func startProvisioning(spim: Data, userID: UUID) async throws -> (RawADIProvisioningSession, Data) { var url = url.appendingPathComponent("v3/provisioning_session") var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! diff --git a/Sources/XKit/GrandSlam/Anisette/XADIProvider.swift b/Sources/XKit/GrandSlam/Anisette/XADIProvider.swift index b1bd2420..04addfb9 100644 --- a/Sources/XKit/GrandSlam/Anisette/XADIProvider.swift +++ b/Sources/XKit/GrandSlam/Anisette/XADIProvider.swift @@ -84,12 +84,6 @@ public actor XADIProvider: RawADIProvider { try await loadTask.value } - public func clientInfo() async throws -> String { - """ - - """ - } - public func startProvisioning(spim: Data, userID: UUID) async throws -> (any RawADIProvisioningSession, Data) { try await loadADI(id: userID) diff --git a/Sources/XKit/GrandSlam/Lookup/GrandSlamLookupManager.swift b/Sources/XKit/GrandSlam/Lookup/GrandSlamLookupManager.swift index 4301e1db..76dd1350 100644 --- a/Sources/XKit/GrandSlam/Lookup/GrandSlamLookupManager.swift +++ b/Sources/XKit/GrandSlam/Lookup/GrandSlamLookupManager.swift @@ -40,7 +40,7 @@ actor GrandSlamLookupManager { var request = HTTPRequest(url: Self.lookupURL) request.headerFields = [ .userAgent: deviceInfo.clientInfo.userAgent, - .init(DeviceInfo.clientInfoKey)!: deviceInfo.clientInfo.clientString, + .init(DeviceInfo.clientInfoKey)!: DeviceInfo.ClientInfo.urlBagClientString, .init(DeviceInfo.deviceIDKey)!: deviceInfo.deviceID, .init(AnisetteData.iLocaleKey)!: Locale.current.identifier, .init(AnisetteData.timeZoneKey)!: TimeZone.current.identifier, diff --git a/Sources/XKit/GrandSlam/Requests/GrandSlamOperationRequest.swift b/Sources/XKit/GrandSlam/Requests/GrandSlamOperationRequest.swift index ca30b7df..befd0c3d 100644 --- a/Sources/XKit/GrandSlam/Requests/GrandSlamOperationRequest.swift +++ b/Sources/XKit/GrandSlam/Requests/GrandSlamOperationRequest.swift @@ -83,7 +83,8 @@ extension GrandSlamOperationRequest { "svct": "iCloud", "loc": Locale.current.identifier ] - clientData.merge(deviceInfo.dictionary) { _, b in b } + // Don't include deviceInfo.dictionary: its Mac hardware identifiers (ROM/MLB/serial) + // contradict the Android client info and Anisette data. clientData.merge(anisetteData.dictionary) { _, b in b } var request: [String: Any] = [ diff --git a/Sources/XToolSupport/AuthCommand.swift b/Sources/XToolSupport/AuthCommand.swift index e10aac3a..2d3ba5f6 100644 --- a/Sources/XToolSupport/AuthCommand.swift +++ b/Sources/XToolSupport/AuthCommand.swift @@ -78,7 +78,7 @@ struct AuthOperation { private func logInWithPassword() async throws -> AuthToken { print(""" Heads up: - This works by emulating a Pixel phone/Windows PC to interact with Apple services; + This works by emulating a Pixel phone to interact with Apple services; consider using a secondary Apple ID. Credentials are only ever sent to Apple. """)