From 8f27efa5852b2105daa0fce73be2d972c96bc7ba Mon Sep 17 00:00:00 2001 From: Daan-25 <180635521+Daan-25@users.noreply.github.com> Date: Thu, 5 Nov 2026 17:24:39 +0100 Subject: [PATCH] [AltServer] Identifies as current Mac to fix sign-in with Advanced Data Protection Public anisette servers report macOS 10.12.6 as client info. Accounts with Advanced Data Protection can't sign in from devices below macOS 13.1, so the 2FA code never arrives and manually generated codes are rejected. Apple accepts the servers' anisette data with a current client info, so AltServer now sends this Mac's model, macOS version and build, along with a matching akd user agent. Setting UseAnisetteServerClientInfo restores the previous behavior. Fixes #1849 --- .../RemoteAnisetteDataFetcher.swift | 26 +++++++++++++++++++ .../Extensions/UserDefaults+AltServer.swift | 6 +++++ 2 files changed, 32 insertions(+) diff --git a/AltServer/Anisette Data/RemoteAnisetteDataFetcher.swift b/AltServer/Anisette Data/RemoteAnisetteDataFetcher.swift index 8f2706735b..3515361bae 100644 --- a/AltServer/Anisette Data/RemoteAnisetteDataFetcher.swift +++ b/AltServer/Anisette Data/RemoteAnisetteDataFetcher.swift @@ -294,8 +294,34 @@ private extension RemoteAnisetteDataFetcher guard !response.clientInfo.isEmpty, !response.clientInfo.contains("\r"), !response.clientInfo.contains("\n"), !response.userAgent.isEmpty, !response.userAgent.contains("\r"), !response.userAgent.contains("\n") else { throw AnisetteError.remoteServerInvalidResponse(serverURL: serverURL, debugDescription: "The server must supply a client_info and a user_agent.") } + + // Public anisette servers identify as macOS 10.12, which accounts with Advanced Data Protection can't sign in from + // (the 2FA code never arrives, and manually generated codes are rejected). Apple accepts their anisette data + // with a current client info, so identify as this Mac instead. + if !UserDefaults.standard.usesAnisetteServerClientInfo, let currentMac = self.currentMacClientInfo() + { + return currentMac + } + return (response.clientInfo, response.userAgent) } + + func currentMacClientInfo() -> (clientInfo: String, userAgent: String)? + { + guard let deviceModel = ProcessInfo.processInfo.deviceModel, + let buildVersion = ProcessInfo.processInfo.operatingSystemBuildVersion, + let cfNetworkVersion = Bundle(identifier: "com.apple.CFNetwork")?.infoDictionary?["CFBundleVersion"] as? String + else { return nil } + + var systemInfo = utsname() + uname(&systemInfo) + let darwinVersion = withUnsafeBytes(of: &systemInfo.release) { String(decoding: $0.prefix(while: { $0 != 0 }), as: UTF8.self) } + + let osVersion = ProcessInfo.processInfo.operatingSystemVersion.stringValue + let clientInfo = "<\(deviceModel)> " + let userAgent = "akd/1.0 CFNetwork/\(cfNetworkVersion) Darwin/\(darwinVersion)" + return (clientInfo, userAgent) + } enum HeadersResult { diff --git a/AltServer/Extensions/UserDefaults+AltServer.swift b/AltServer/Extensions/UserDefaults+AltServer.swift index 5e47d645ff..09a20b381e 100644 --- a/AltServer/Extensions/UserDefaults+AltServer.swift +++ b/AltServer/Extensions/UserDefaults+AltServer.swift @@ -60,6 +60,7 @@ extension UserDefaults { private static let altJITTimeoutKey = "JITTimeout" private static let anisetteServerURLKey = "RemoteAltServerURL" + private static let usesAnisetteServerClientInfoKey = "UseAnisetteServerClientInfo" var altJITTimeout: TimeInterval? { let timeout = self.double(forKey: UserDefaults.altJITTimeoutKey) // Coerces strings into doubles. @@ -71,4 +72,9 @@ extension UserDefaults var anisetteServerURLOverride: URL? { return self.string(forKey: UserDefaults.anisetteServerURLKey).flatMap { URL(string: $0) } } + + // Opts out of identifying as this Mac, and uses the anisette server's (outdated) client info instead. + var usesAnisetteServerClientInfo: Bool { + return self.bool(forKey: UserDefaults.usesAnisetteServerClientInfoKey) + } }