Skip to content

Handle 2FA-required errors separately from password errors in iCloud - #181390

Open
TeroPihlaja wants to merge 1 commit into
home-assistant:devfrom
TeroPihlaja:icloud/2fa-aware-setup-errors
Open

Handle 2FA-required errors separately from password errors in iCloud#181390
TeroPihlaja wants to merge 1 commit into
home-assistant:devfrom
TeroPihlaja:icloud/2fa-aware-setup-errors

Conversation

@TeroPihlaja

@TeroPihlaja TeroPihlaja commented Sep 5, 2026

Copy link
Copy Markdown

Proposed change

A 2FA challenge and a rejected password both end up in the same error path during setup, so a routine 2FA re-challenge is reported to the user as "your iCloud account is no longer working" — pointing them at the password instead of the verification code.

This reports the two cases separately, and keeps the authenticated session when the account only needs a code. That part matters beyond the wording: async_step_reauth reads self.api = self._get_reauth_entry().runtime_data.api and, when it is None, falls back to _show_setup_form() to ask for the password again. Dropping the session on a 2FA challenge would therefore send the user back to re-enter a password that was never wrong, instead of straight to the code entry.

It also handles the session being rejected between logging in and the first device fetch. Reading api.devices refreshes the session, and a stored token that iCloud has since invalidated is only rejected at that point rather than while logging in — surfacing as PyiCloudFailedLoginException: Invalid authentication token. or PyiCloudAuthRequiredException. Previously both escaped as an unhandled traceback with no reauth flow started, so the user got a failed entry and nothing to act on. That path still raises ConfigEntryNotReady so the entry is retried with backoff: the fetch timer is only armed once update_devices() completes, so returning early there would leave an entry that is loaded and never polls again.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:
  • Link to developer documentation pull request:
  • Link to frontend pull request:

Supersedes #169005, which was closed by the stale bot rather than on review. Rebuilt as a single commit on current dev.

On #169005 it was asked whether this duplicated #169001. That was a fair reading of the old branches — the polling-loop branch carried the 2FA message split as well. The two are now cleanly separated: this PR only classifies the setup-time error, and #181389 only keeps the fetch timer alive. They touch the same file but not the same behaviour.

The existing test_setup_2fa continues to assert that the session survives a 2FA challenge, which is what keeps the reauth flow going straight to code entry.

This needs the bugfix label for the required-labels check to pass — I cannot add it myself.

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

@home-assistant

home-assistant Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Hey there @Quentame, @nzapponi, mind taking a look at this pull request as it has been labeled with an integration (icloud) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of icloud can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant mark-draft Mark the pull request as draft.
  • @home-assistant ready-for-review Remove the draft status from the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign icloud Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant update-branch Update the pull request branch with the base branch.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The mandatory PR-template sections must be restored, and the new tests need minor convention and duplication fixes.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Separates iCloud 2FA challenges from invalid-password failures while preserving sessions needed for verification.

Changes:

  • Adds targeted authentication-error handling and reauth behavior.
  • Adds coverage for 2FA, password, and first-fetch failures.
File summaries
File Description
homeassistant/components/icloud/account.py Classifies authentication failures and preserves 2FA sessions.
tests/components/icloud/test_init.py Tests authentication failure paths and retry behavior.
Review details

Suppressed comments (1)

tests/components/icloud/test_init.py:142

  • Fold this coverage into the existing test_setup_password_failed. That test already performs the same setup, reauth-flow, and cleared-session checks, so the log assertions can be added there instead of duplicating the scenario.
@pytest.mark.usefixtures("service_2fa_failed")
async def test_setup_password_failed_reports_bad_password(
  • Files reviewed: 2/2 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread homeassistant/components/icloud/account.py
Comment thread tests/components/icloud/test_init.py Outdated
Comment thread tests/components/icloud/test_init.py
Copilot AI review requested due to automatic review settings September 5, 2026 16:45
@TeroPihlaja
TeroPihlaja force-pushed the icloud/2fa-aware-setup-errors branch from 5752c06 to 9189eb5 Compare September 5, 2026 16:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused authentication changes are consistent with the reauth flow and adequately tested.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

A 2FA challenge and a rejected password both end up in the same error
path during setup, so a routine 2FA re-challenge was reported to the
user as "your account is no longer working", pointing them at the
password instead of the verification code.

Report the two cases separately and keep the authenticated session when
the account only needs a 2FA code: the reauth flow reuses that session
to send and validate the code, and dropping it would send the user back
to the setup form to re-enter a password that was never wrong.

Also handle PyiCloudAuthRequiredException, which the session can raise
between logging in and the first device fetch. That still raises
ConfigEntryNotReady so the entry is retried, because the fetch timer is
only armed once update_devices() completes and an entry that returned
early here would stay loaded without ever polling again.
@TeroPihlaja
TeroPihlaja force-pushed the icloud/2fa-aware-setup-errors branch from 9189eb5 to d2c4d2f Compare September 5, 2026 18:12
Copilot AI review requested due to automatic review settings September 5, 2026 18:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The authentication paths are correctly distinguished and adequately covered by focused tests.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants