Skip to content

Fix #232: Trusted-device bridge protocol changed, breaking 2FA#233

Draft
ebarrere wants to merge 1 commit intotimlaing:mainfrom
ebarrere:main
Draft

Fix #232: Trusted-device bridge protocol changed, breaking 2FA#233
ebarrere wants to merge 1 commit intotimlaing:mainfrom
ebarrere:main

Conversation

@ebarrere
Copy link
Copy Markdown

@ebarrere ebarrere commented May 7, 2026

Breaking change

NA, as far as I know.

Proposed change

Fix #232 Trusted-device bridge protocol changed, breaking 2FA. Apple appears to have changed the protocol, so we must adapt or be left behind.

Type of change

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

Example of code:

Clear cache, attempt login

rm -rf /var/folders/yg/<SESSION_PATH>/T/pyicloud/
api = PyiCloudService(ICLOUD_EMAIL, refresh_interval=360)

if api.requires_2fa:
    print("Two-factor authentication required.")
    api.request_2fa_code()
    code = input("Enter the code you received of one of your approved devices: ")
    result = api.validate_2fa_code(code)
    print("Code validation result:", result)

Additional information

Checklist

  • 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.
  • Tests have been added to verify that the new code works.

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

  • Documentation added/updated to README

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility with Apple's trusted-device authentication protocols for more reliable authentication
    • Enhanced validation of authentication payloads with improved fallback mechanisms
    • Strengthened robustness of device trust establishment processes

Walkthrough

The hsa2_bridge module now accepts Apple's newer trusted-device bridge push protocol variant that includes flowid as an alternative session identifier to sessionUUID. The push payload validation model supports both fields, resolves the session identifier by prioritising sessionUUID or falling back to flowid, and normalises it into a single session_uuid field. Bootstrap state initialisation now uses the normalised push payload session UUID directly, removing the prior mismatch validation against locally generated UUIDs.

Changes

Bridge flowid Protocol Support

Layer / File(s) Summary
Push Payload Validation Model
pyicloud/hsa2_bridge.py
Adds flow_id as an optional field with flowid alias to _BridgePushPayloadModel; validation ensures both session_uuid (alias sessionUUID) and flow_id are non-blank when present.
Session Identifier Normalisation
pyicloud/hsa2_bridge.py
BridgePushPayload.from_payload resolves the session identifier by selecting session_uuid or falling back to flow_id; raises an error if neither is present and sets the normalised session_uuid.
Bootstrap State Initialisation
pyicloud/hsa2_bridge.py
TrustedDeviceBridgeState initialisation now sets session_uuid directly from the normalised push_payload.session_uuid and removes the prior mismatch check against locally generated UUIDs.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

A bridge once relied on one path alone,
But Apple changed what it made known.
Now flow and session dance as one,
Fallback and grace—the puzzle's done! 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: fixing the 2FA issue caused by Apple's protocol change from sessionUUID to flowid in the trusted-device bridge.
Description check ✅ Passed The description is directly related to the changeset, explaining the proposed fix for the breaking protocol change and providing reproduction steps and test code.
Linked Issues check ✅ Passed The code changes directly address issue #232 by supporting the new flowid protocol alongside sessionUUID, validating the payload correctly, and normalizing the session identifier during bridge bootstrap.
Out of Scope Changes check ✅ Passed All changes in the PR are scoped to fixing the trusted-device bridge protocol issue; there are no unrelated modifications or out-of-scope alterations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Pylint (4.0.5)
pyicloud/hsa2_bridge.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ebarrere ebarrere marked this pull request as draft May 7, 2026 16:59
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pyicloud/hsa2_bridge.py (1)

220-229: ⚡ Quick win

Add regression tests for both identifier variants

Please add unit tests for: (1) payload with sessionUUID, (2) payload with only flowid, and (3) payload with neither. This path is protocol-sensitive and currently untested in this PR.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyicloud/hsa2_bridge.py` around lines 220 - 229, Add unit tests covering the
session identifier resolution in the HSA2 bridge: (1) a payload containing
sessionUUID should result in cls being constructed with session_uuid equal to
validated.session_uuid, (2) a payload containing only flowid should result in
session_uuid equal to validated.flow_id, and (3) a payload missing both should
raise PyiCloudTrustedDevicePromptException. Target the code path that sets
resolved_session_uuid from validated.session_uuid or validated.flow_id and the
constructor call cls(payload=..., session_uuid=...), asserting the expected
session_uuid or exception for each case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyicloud/hsa2_bridge.py`:
- Around line 159-165: The validator _validate_session_uuid currently raises
ValueError for blank session identifiers which prevents flow_id fallback; change
its behavior to treat blank strings as None instead of raising: in the
`@field_validator` for "session_uuid" and "flow_id" (function
_validate_session_uuid) detect if value is not None and value.strip() is empty,
and return None rather than raising an error, keeping the return type
Optional[str] so a present-but-blank session_uuid will be treated as missing and
allow flow_id fallback to proceed.

---

Nitpick comments:
In `@pyicloud/hsa2_bridge.py`:
- Around line 220-229: Add unit tests covering the session identifier resolution
in the HSA2 bridge: (1) a payload containing sessionUUID should result in cls
being constructed with session_uuid equal to validated.session_uuid, (2) a
payload containing only flowid should result in session_uuid equal to
validated.flow_id, and (3) a payload missing both should raise
PyiCloudTrustedDevicePromptException. Target the code path that sets
resolved_session_uuid from validated.session_uuid or validated.flow_id and the
constructor call cls(payload=..., session_uuid=...), asserting the expected
session_uuid or exception for each case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71295618-c989-442c-9904-47fa496e02f5

📥 Commits

Reviewing files that changed from the base of the PR and between 7abec01 and 06f705f.

📒 Files selected for processing (1)
  • pyicloud/hsa2_bridge.py

Comment thread pyicloud/hsa2_bridge.py
Comment on lines +159 to 165
@field_validator("session_uuid", "flow_id")
@classmethod
def _validate_session_uuid(cls, value: str) -> str:
def _validate_session_uuid(cls, value: Optional[str]) -> Optional[str]:
"""Reject blank bridge session identifiers."""
if not value.strip():
raise ValueError("sessionUUID must not be blank")
if value is not None and not value.strip():
raise ValueError("sessionUUID/flowid must not be blank")
return value
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Allow fallback when sessionUUID is present-but-blank

Right now, a blank sessionUUID raises validation even if flowid is valid. Treat blank identifiers as None so flowid fallback can still work.

Suggested patch
 `@field_validator`("session_uuid", "flow_id")
 `@classmethod`
 def _validate_session_uuid(cls, value: Optional[str]) -> Optional[str]:
     """Reject blank bridge session identifiers."""
-    if value is not None and not value.strip():
-        raise ValueError("sessionUUID/flowid must not be blank")
-    return value
+    if value is None:
+        return None
+    normalized = value.strip()
+    return normalized or None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyicloud/hsa2_bridge.py` around lines 159 - 165, The validator
_validate_session_uuid currently raises ValueError for blank session identifiers
which prevents flow_id fallback; change its behavior to treat blank strings as
None instead of raising: in the `@field_validator` for "session_uuid" and
"flow_id" (function _validate_session_uuid) detect if value is not None and
value.strip() is empty, and return None rather than raising an error, keeping
the return type Optional[str] so a present-but-blank session_uuid will be
treated as missing and allow flow_id fallback to proceed.

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.

Trusted-device bridge protocol changed, breaking 2FA

1 participant