mirror of
https://github.com/fenleon/open-madrid.git
synced 2026-10-22 16:12:35 +00:00
Registration: PET fallback to the spd token map (the live-confirmed post-2FA delivery channel — the header was absent on that path); sign-in failures carry the response error shape
This commit is contained in:
1 parent
9e00f95e3b
commit
322e24c6d2
2 files changed
+43
-4
No files matched your search
@@ -164,11 +164,34 @@ class GsaDelegateLoginClient(
|
||||
val encoded = XmlPlist.encode(body)
|
||||
val response = http.post(endpoint, headers, encoded, CONTENT_TYPE)
|
||||
if (response.status != 200) {
|
||||
throw GsaLoginException("iCloud sign-in POST $endpoint → HTTP ${response.status}")
|
||||
throw GsaLoginException(
|
||||
"iCloud sign-in POST $endpoint → HTTP ${response.status}${errorDetail(response.body)}",
|
||||
)
|
||||
}
|
||||
return parseResponse(response.body)
|
||||
}
|
||||
|
||||
/** Top-level error-shape facts only (keys, integer codes, error strings) — never token values. */
|
||||
private fun errorDetail(bytes: ByteArray): String {
|
||||
val parsed = try {
|
||||
Plist.parse(bytes)
|
||||
} catch (_: Exception) {
|
||||
return ""
|
||||
}
|
||||
val dict = stringKeyedDictOrNull(parsed) ?: return " (${typeName(parsed)})"
|
||||
val scalars = dict.entries
|
||||
.mapNotNull { (k, v) ->
|
||||
when {
|
||||
v is Int || v is Long -> "$k=$v"
|
||||
v is String && k !in setOf("dsid", "auth-token", "adsid") -> "$k=$v"
|
||||
v is Map<*, *> -> "$k keys=${stringKeyedDictOrNull(v)?.keys ?: v.keys}"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.joinToString(", ")
|
||||
return " — body: $scalars"
|
||||
}
|
||||
|
||||
private fun parseResponse(bytes: ByteArray): IcloudSignInResponse {
|
||||
val parsed = try {
|
||||
Plist.parse(bytes)
|
||||
|
||||
@@ -181,14 +181,27 @@ class GsaLoginChain(
|
||||
val spd = complete.spd?.let { GsaSpd.parse(GsaSpdDecryptor.decrypt(it, proof.k)) }
|
||||
return when (val secondaryAuth = complete.secondaryAuth) {
|
||||
null -> {
|
||||
val result = GsaLoginResult.Authenticated(spd = spd, pet = complete.pet)
|
||||
// The PET rides the response headers when Apple sends them; on the post-2FA
|
||||
// SRP re-login it arrives instead inside the spd token map under the PET
|
||||
// service id (observed live — the header was absent on that path). A no-2FA
|
||||
// login's spd carries only com.apple.gs.appleid.auth and no PET.
|
||||
val pet = complete.pet
|
||||
?: spd?.tokens[PET_SERVICE_ID]?.let {
|
||||
GsaPet(
|
||||
it.token,
|
||||
it.expiresAtEpochMs
|
||||
?: (clock() + GsaTokenHeaders.PET_DEFAULT_EXPIRY_SECONDS * 1000),
|
||||
)
|
||||
}
|
||||
logEvent("gsa: pet=${complete.pet != null} spdToken=${pet != null} tokens=${spd?.tokens?.keys}")
|
||||
val result = GsaLoginResult.Authenticated(spd = spd, pet = pet)
|
||||
credentialStore?.save(
|
||||
GsaPersistedCredentials(
|
||||
username = username,
|
||||
passwordHash = passwordHash,
|
||||
adsid = spd?.adsid,
|
||||
petToken = complete.pet?.token,
|
||||
petExpiresAtEpochMs = complete.pet?.expiresAtEpochMs,
|
||||
petToken = pet?.token,
|
||||
petExpiresAtEpochMs = pet?.expiresAtEpochMs,
|
||||
),
|
||||
)
|
||||
result
|
||||
@@ -344,5 +357,8 @@ class GsaLoginChain(
|
||||
companion object {
|
||||
const val GSA_STATUS_SECONDARY = GsaStatus.AU_SECONDARY_AUTH
|
||||
const val GSA_STATUS_TRUSTED = GsaStatus.AU_TRUSTED_DEVICE
|
||||
|
||||
/** The spd token-map service id under which the PET is delivered (§1.5 PET). */
|
||||
const val PET_SERVICE_ID = "com.apple.gs.idms.pet"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user