Skip to content

Commit

Permalink
sms mfa using str instead of int to keep leading zeros
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyNikiforov committed Nov 11, 2024
1 parent 1aec515 commit 47c0a84
Show file tree
Hide file tree
Showing 7 changed files with 473 additions and 979 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- fix: sms MFA dropping leading zeros [#993](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/993)

## 1.24.3 (2024-11-03)

- fix: crashes when no imagetype sent by Apple [ref](https://github.com/boredazfcuk/docker-icloudpd/issues/680)
Expand Down
29 changes: 21 additions & 8 deletions src/icloudpd/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,16 @@ def request_2fa(icloud: PyiCloudService, logger: logging.Logger) -> None:
if not icloud.send_2fa_code_sms(device.id):
logger.error("Failed to send two-factor authentication code")
sys.exit(1)
code: int = click.prompt(
"Please enter two-factor authentication code that you received over SMS",
type=click.IntRange(0, 999999),
)
while True:
code: str = click.prompt(
"Please enter two-factor authentication code that you received over SMS",
).strip()
if len(code) == 6:
if code.isdigit():
break
else:
click.echo("Invalid code, should be six digits. Try again")
continue
if not icloud.validate_2fa_code_sms(device.id, code):
logger.error("Failed to verify two-factor authentication code")
sys.exit(1)
Expand All @@ -200,10 +206,17 @@ def request_2fa(icloud: PyiCloudService, logger: logging.Logger) -> None:
logger.error("Failed to verify two-factor authentication code")
sys.exit(1)
else:
code = click.prompt(
"Please enter two-factor authentication code", type=click.IntRange(0, 999999)
)
if not icloud.validate_2fa_code(str(code)):
while True:
code = click.prompt(
"Please enter two-factor authentication code",
).strip()
if len(code) == 6:
if code.isdigit():
break
else:
click.echo("Invalid code, should be six digits. Try again")
continue
if not icloud.validate_2fa_code(code):
logger.error("Failed to verify two-factor authentication code")
sys.exit(1)
logger.info(
Expand Down
2 changes: 1 addition & 1 deletion src/pyicloud_ipd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ def validate_verification_code(self, device: Dict[str, Any], code: str) -> bool:

return not self.requires_2sa

def validate_2fa_code_sms(self, device_id: int, code:int) -> bool:
def validate_2fa_code_sms(self, device_id: int, code:str) -> bool:
"""Verifies a verification code received via Apple's 2FA system through SMS."""

oauth_session = self.get_oauth_session()
Expand Down
4 changes: 2 additions & 2 deletions src/pyicloud_ipd/sms.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,12 @@ def build_send_sms_code_request(context: _TrustedPhoneContextProvider, device_id
json = json)
return req

def build_verify_sms_code_request(context: _TrustedPhoneContextProvider, device_id: int, code: int) -> Request:
def build_verify_sms_code_request(context: _TrustedPhoneContextProvider, device_id: int, code: str) -> Request:
""" Builds a request for the list of trusted phone numbers for sms 2fa """

url = _auth_url(context.domain) + "/verify/phone/securitycode"

json = {"phoneNumber":{"id":device_id},"securityCode":{"code":str(code)},"mode":"sms"}
json = {"phoneNumber":{"id":device_id},"securityCode":{"code":code},"mode":"sms"}

req = _InternalRequest(
method="POST",
Expand Down
39 changes: 39 additions & 0 deletions tests/test_two_step_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,42 @@ def test_2fa_flow_valid_code(self) -> None:
self._caplog.text,
)
assert result.exit_code == 0

def test_2fa_flow_valid_code_zero_lead(self) -> None:
base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3])
cookie_dir = os.path.join(base_dir, "cookie")

for dir in [base_dir, cookie_dir]:
recreate_path(dir)

with vcr.use_cassette(os.path.join(self.vcr_path, "2fa_flow_valid_code_zero_lead.yml")):
runner = CliRunner(env={"CLIENT_ID": "DE309E26-942E-11E8-92F5-14109FE0B321"})
result = runner.invoke(
main,
[
"--username",
"jdoe@gmail.com",
"--password",
"password1",
"--no-progress-bar",
"--cookie-directory",
cookie_dir,
"--auth-only",
],
input="054321\n",
)
self.assertIn("DEBUG Authenticating...", self._caplog.text)
self.assertIn(
"INFO Two-factor authentication is required",
self._caplog.text,
)
self.assertIn(
"Please enter two-factor authentication code or device index (a) to send SMS with a code: 054321",
result.output,
)
self.assertIn(
"INFO Great, you're all set up. The script can now be run without "
"user interaction until 2FA expires.",
self._caplog.text,
)
assert result.exit_code == 0
Loading

0 comments on commit 47c0a84

Please sign in to comment.