Skip to content

fix: update auth endpoint + stale lock file cleanup#486

Open
ricky0603 wants to merge 2 commits into
majd:mainfrom
ricky0603:fix/apple-auth-endpoint-hotfix24
Open

fix: update auth endpoint + stale lock file cleanup#486
ricky0603 wants to merge 2 commits into
majd:mainfrom
ricky0603:fix/apple-auth-endpoint-hotfix24

Conversation

@ricky0603
Copy link
Copy Markdown

@ricky0603 ricky0603 commented Jun 6, 2026

Problem

Apple recently changed their authentication infrastructure in a way that breaks ipatool login for all accounts:

  1. The authenticateAccount key moved in Bag XML — it used to live inside the urlBag dict; it now appears at the root level of the plist. ipatool's bagResult struct only looked inside urlBag, so Bag() always returned an empty endpoint.

  2. New auth domain — the endpoint itself changed from buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate to auth.itunes.apple.com/auth/v1/native. The Bag XML signals that the /fast sub-path should be used via auth/v1/native: ["fast"].

  3. Missing fallback — when Bag() returned an empty string, loginRequest passed it as-is to the HTTP client, which panicked. There was no hardcoded fallback for the new domain.

Symptom: every login returns HTTP 200 with an empty body in ~1 ms, which surfaces as "something went wrong".

  1. Stale 0-byte cookie lock file (independent bug)juju/persistent-cookiejar uses juju/go4/lock to serialize cookie file access. If a process is killed between lock file creation (O_TRUNC) and PID write, a 0-byte .lock file remains. The library only does PID-based stale detection when fi.Size() > 0; a 0-byte file causes every subsequent startup to fail with "file locked for too long; giving up" until the file is manually deleted.

Changes

pkg/appstore/appstore_bag.go

  • Add AuthEndpoint string field at the root level of bagResult (plist key authenticateAccount) so the new Bag layout is parsed correctly. The urlBag path is kept as a fallback for backward compatibility.
  • Append /fast to any endpoint URL on auth.itunes.apple.com (matching the Bag's auth/v1/native: ["fast"] hint).

pkg/appstore/appstore_login.go

  • Use util.IfEmpty(endpoint, fallback) in loginRequest so that if Bag() fails or returns empty, the new auth.itunes.apple.com/auth/v1/native/fast endpoint is used instead of an empty string.

pkg/appstore/constants.go

  • Add PrivateAuthDomain (auth.itunes.apple.com) and PrivateAuthPathNative (/auth/v1/native/fast) constants.

cmd/common.go

  • In newCookieJar(), stat the lock path before opening the jar and remove it if it is zero bytes. A live process always writes its PID immediately after creating the lock file, so a 0-byte lock is always stale.

Testing

Verified against a live Apple ID after the infrastructure change — all accounts authenticate successfully and purchased=true success=true is returned for downloads.


Summary by cubic

Fixes login by updating to Apple’s new auth endpoint and adding a safe fallback, and prevents startup failures by cleaning stale cookie lock files.

  • Bug Fixes
    • Apple auth: read authenticateAccount from the Bag root, fall back to urlBag, append /fast for auth.itunes.apple.com, and default to https://auth.itunes.apple.com/auth/v1/native/fast when Bag is empty.
    • Cookies: before creating the jar, delete zero-byte <cookies>.lock files to avoid “file locked for too long” errors from juju/persistent-cookiejar and juju/go4/lock.

Written for commit 5eeda95. Summary will update on new commits.

Review in cubic

wl added 2 commits June 7, 2026 06:15
Apple's authentication service moved the endpoint from
buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate
to auth.itunes.apple.com/auth/v1/native/fast, and simultaneously
moved the `authenticateAccount` key in the Bag XML from inside the
`urlBag` dict to the root level.

Changes:
- bagResult: add root-level `authenticateAccount` plist field so the
  Bag() function reads it before falling back to urlBag (backward compat)
- Bag(): append /fast suffix when the resolved endpoint uses the new
  auth.itunes.apple.com domain, matching what the Bag XML signals via
  `auth/v1/native: ["fast"]`
- constants: add PrivateAuthDomain / PrivateAuthPathNative for the new
  endpoint
- loginRequest: use the new auth endpoint as the hardcoded fallback
  (replaces the implicit empty-string which caused "something went wrong"
  when Bag lookup returned nothing)

Without this fix every login attempt returns HTTP 200 with an empty body
in ~1 ms, which the code surfaces as "something went wrong".
The juju/persistent-cookiejar library (via juju/go4/lock) creates a
lock file at <cookies>.lock, writing the owner PID as JSON. If a
process is killed between file creation (O_TRUNC) and the PID write,
a 0-byte file remains. On the next startup the library skips PID-based
stale detection (only runs when fi.Size() > 0) and falls back to
os.OpenFile with O_EXCL, which fails because the file already exists.
The result is "cannot load cookies: file locked for too long; giving up"
on every subsequent invocation until the file is manually removed.

Fix: in newCookieJar(), stat the lock path before opening the jar and
remove the file if it is zero bytes. Zero-byte lock files are always
stale because a live process always writes its PID immediately after
creating the file.
@void-eth
Copy link
Copy Markdown

void-eth commented Jun 7, 2026

pulled these changes into a local clone and tested against my Apple ID. The auth endpoint fix is clearly working now, the request actually reaches Apple's auth server instead of returning the old empty-body "something went wrong" error.

I did hit a rate limit on the 2FA step though:

6:25PM INF enter 2FA code: 828232
6:25PM ERR error="request failed: rate limited by Apple (HTTP 429): Rate limit has been exceeded for: mzauth|global|all" success=false

To be clear, this 429 is coming from Apple's side (mzauth|global|all), not from anything in the patch. Before these changes the login never got far enough to even talk to the new endpoint, so reaching the point where Apple rate-limits us is actually a good sign that the new auth.itunes.apple.com/auth/v1/native/fast path is being used correctly. I'd been retrying logins a fair bit while testing, which probably tripped the limit. I'll wait it out and retry to confirm a clean end-to-end login.

Thanks for putting this together.

@BeauGiles
Copy link
Copy Markdown

also getting the "Rate limit has been exceeded" error, even on a fresh account that hasn't been logged into for... weeks.

@umonaca
Copy link
Copy Markdown

umonaca commented Jun 7, 2026

I did hit a rate limit on the 2FA step though:

With a residential proxy IP or my real residential IP, I have successfully logged in and downloaded IPA. No 429 error.

With the following command you can test if your IP is being restricted or rate limited by Apple:

curl -H "Configurator/2.17 (Macintosh; OS X 15.3.1; 24D70) AppleWebKit/0620.1.16.11.6" -v "https://auth.itunes.apple.com/auth/v1/native/fast"

If you get the following response and a 429 error, your IP is rate limited. Otherwise the response body will be empty (with 404 status code).

Rate limit has been exceeded for: mzauth|global|all

@FimastGDx
Copy link
Copy Markdown

thx u bro

umonaca added a commit to umonaca/ipatool that referenced this pull request Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants