Skip to content

Sign-in never completes: 2FA code accepted but the state stays REQUIRE_2FA on a secondaryAuth account #236

Open

Description

@davidcoulson

Exporter 1.5.1 from source (02fd88e), macOS 26.7, iCloud route.

Sign-in reaches 2FA, I enter the code, Apple takes it — and then the re-auth behind it comes back still asking for 2FA. It loops until it gives up.

INFO  Detected 2FA requirement: secondaryAuth
INFO  Transitioning login state: LoginState.REQUIRE_2FA -> LoginState.REQUIRE_2FA
INFO  Apple took the code and then failed to finish signing in:
      Unexpected state after submitting 2FA: LoginState.REQUIRE_2FA

Not #168. There is no 503 anywhere in the log and I'm on the akd fix. I checked the edge block separately and it behaves as you described: Xcode gets 503, akd gets 401.

My account comes back as au = secondaryAuth rather than trustedDeviceSecondaryAuth, so only SMS was offered. Six attempts over an hour, with the built-in anisette and with a local anisette-v3 server, identical every time.

So I patched trusted_device_2fa = True to force the trusted-device flow. Got the code on my Mac, entered it, same result exactly. It isn't the SMS channel.

Two things worth separating:

  1. The sign-in never completes on a secondaryAuth account.
  2. Whatever the cause, it gets classified as the spent-code 503 path, so it waits and sends a fresh code, over and over. I burned a dozen codes before reading the log and realising nothing was going to change.

Happy to run anything you want to try.

Activity

  1. parawanderer commented on Sep 19, 2026

    @parawanderer
    Owner

    Two separate faults, both on our side.

    The exporter treats any UnhandledProtocolError after the submit as the #168 spent-code case (code_was_already_spent, python/exporter/icloud.py:538). So "Apple asked for 2FA again" gets handled as "wait and send a new code". That's why you burned a dozen codes. I'll narrow it separately.

    Likely cause of the loop: at the pinned FindMy.py fork, the Grand Slam sign-in identifies as com.apple.akd, but every 2FA call (/auth/verify/phone, …/securitycode, /auth/verify/trusteddevice, GsService2/validate) still sends the Xcode client info, X-Apple-App-Info: com.apple.gs.xcode.auth and User-Agent: Xcode. So the code is verified for one client, and the re-auth that follows comes from another. This is unconfirmed: another user got through 2FA with the same mismatch, so it doesn't break every account.

    experiment/236-akd-2fa-headers on the fork makes the 2FA calls identify as akd too. It's one commit, 0768ded, on top of the version the exporter pins. To test it, from python/:

    uv pip install "FindMy @ git+https://github.com/parawanderer/FindMy.py@experiment/236-akd-2fa-headers"
    uv run --no-sync python -m exporter.wizard

    The install replaces your trusted_device_2fa = True edit, so SMS is the only thing that differs. --no-sync matters: plain uv run reinstalls the locked FindMy.py and drops the change.

    Try one sign-in. If the log shows Apple took the code and then failed to finish signing in, stop it there with Ctrl-C, or the loop will spend more codes. Either outcome is useful. Please send the log lines from Detected 2FA requirement to the end.

    To go back: uv sync --frozen.

    Drafted by Claude Code, reviewed by @parawanderer

  2. davidcoulson commented on Sep 19, 2026

    @davidcoulson
    Author

    Tried experiment/236-akd-2fa-headers (confirmed installed at 0768ded) with main at 335b258, SMS 2FA, via the wizard. Same result: the code is accepted, the re-auth comes back asking for secondaryAuth again.

    07:48:04 anisette: Provisioning...
    07:48:06 account: Detected 2FA requirement: secondaryAuth
    07:48:06 account: LoginState.LOGGED_OUT -> LoginState.REQUIRE_2FA
    07:48:21 account: Attempting authentication for user <redacted>
    07:48:21 account: Detected 2FA requirement: secondaryAuth
    07:48:21 account: LoginState.REQUIRE_2FA -> LoginState.REQUIRE_2FA
    ERROR Reading accessories failed
      icloud.py:584 _submit_code_with_retries -> twofactor.py:173 submit
      -> account.py:1023 sms_2fa_submit
    findmy.errors.UnhandledProtocolError: Unexpected state after submitting 2FA: LoginState.REQUIRE_2FA
    

    On the good side, #237 worked: it stopped after one code instead of looping.

  3. added
    bugSomething isn't working
    @appIssues regarding the OpenTagViewer Android app
    on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    @appIssues regarding the OpenTagViewer Android appbugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions