picklepete/pyicloud

Two-factor authentication (2FA) not implemented

Bart274 opened this issue ยท 60 comments

Hi,
I use pyicloud in the device_tracker entity in Home Assistant.
My users have noticed 2factor authentication isn't working, it works for 2steps authentication?

Instead of basing ourselves by the hsaChallengeRequired key from the API data, can't we base ourselves at these keys: data.dsInfo.hsaEnabled and data.dsInfo.hsaVersion.
For 2 factor authentication, the key hsaEnabled is True and the key hsaVersion is 2. I suspect the hsaVersion is 1 for 2 step authentication.

@Bart274 did you find any solution to achieve 2FA?

we can't implement it? is there any way?

I have been able to get this code to work for my Apple ID (on which two-factor authentication has been enabled):
https://github.com/MikeTheCanuck/CloudContactUpdate/blob/master/Experiments%20in%20authenticating%20to%20iCloud%20using%20pyicloud.ipynb

What is the issue you're experiencing? Maybe I can help by reproducing in my code, or help isolate the difference you might have between your code and mine?

Hello Mike,

While implementing it. It returns unauthorized response

Thanks but I don't know how to reproduce the outcome either of you are experiencing without much greater detail (and it's likely that the project participants wouldn't be able to either).

To have a fair chance at reproducing your situations, I recommend you supply your issue report with the following:

  • A description of the specific outcome you are expecting to have
  • A description of the specific outcome you are actually experiencing
  • A step-by-step guide to the procedure you're following including at what point
  • A detailed description of your environment (operating system, version of Python being used, versions of libraries being used, version of PyiCloud being used, and any other components that you think might be relevant to reproducing your scenario)
  • If possible, the code/script that you're using that causes the failure
Fonta commented

When will you support 2 factor authentication?
I would like to use iCloud for presence detection in home-assistant, but without support for 2FA, my iPhone keeps giving popups that someone used my credentials to login.
With that this plugin is unusable, because disabling 2FA on my account makes Homekit externally unavailable.

With the current code, if 2FA is enabled, we will fall back to the 2SA code-path, requesting a validation code from a trusted device, e.g. via SMS, and once you are fully authorised the cookie will be saved for future authorisations, so you shouldn't see the popup every time.

@f0nt4 also, please keep home-assistant issues to the relevant bug tracker. pyicloud is a library and as long as the library provides the primitives to give a authentication workflow that works with 2FA it is up to the home-assistant plugin to implement it.

@Bart274: I switched my account over from 2SA to 2FA, and I have a single trusted device presented when going through the 2SA code path, which is the SMS option. Perhaps you don't have a mobile number associated with your account? With 2FA enabled I remember logging in to icloud.com and clicking "Did not get a verification code? ", and then choosing the SMS option, that might have added SMS as a trusted option? Can you try that?

@Bart274 I'm using your icloud component in HA currently and have iCloud 2FA (NOT 2Step) working, but only for a few hours. If I take the code provided by the 2FA prompt from my MBP or iPhone, append it to the end of my iCloud password in the configuration.yaml file it will authenticate for the first time using the pyicloud library. The second time around when it goes to check for updated info for my Apple devices, it will say:

pyicloud.exceptions.PyiCloudFailedLoginException: ('Invalid email/password combination.', PyiCloudAPIResponseError('Misdirected Request',))

If I remove the 6 digit code I appended to the end of my password, and re-save the configuration.yaml file, the error will go away, authentication will complete successfully:

INFO:pyicloud.base:Authenticating as [email-redacted] INFO:pyicloud.base:Authentication completed successfully

and the credentials are stored for what seems to be 6 hours without any new codes being pushed to my devices. After about 6 hours, I have to go through the auth process described all over again. So, it appears 2FA is working with pyicloud, but it's not caching the authentication for the appropriate amount of time.

Would using a app-specific password work? As I don't believe you can use 2FA with a non Apple product...

I'm having an issue where api.validate_verification_code always returns False even though the code is the one displayed on the iPhone.

I'm slightly unfamiliar with the difference between 2FA and 2SA. Is my scenario part of this issue, or is it some separate bug?

I've clarified the situation in 69af919

Patches to support 2FA welcome!

@torarnv because this is acting as a custom web browser, can we just save the authentication cookies for 2fa? With any other browser when you log in you can check the box to save the browser and not require a code the next time. Can we do something similar?

Patches to support 2FA welcome!

In the meantime, is it possible to support 2SA on an iOS11 device? Apple docs seem to indicate that iOS9+ support 2FA only, so I guess I'd need a really old device to use this, right?

The documentation currently states:

This approach also works if the account is set up for two-factor authentication (2FA), but the authentication will time out after a few hours. Full support for two-factor authentication (2FA) is not implemented in PyiCloud yet. See issue #102.

But after multiple attempts, using the 2FA with that code example, it always returns Failed to verify verification code.

Could anyone help me? I cannot disable "Two-Factor Authentification" in my settings. Looks like if you changed it to 2FA once, there is no way back(If you know how to do it, please tell me). The two step thing worked for me just one time. The second time I try to rerun the code I get "pyicloud.exceptions.PyiCloudAPIResponseError: Missing X-APPLE-WEBAUTH-HSA-LOGIN cookie". Is this a known issue?

Thanks

Same here as @mariusciziunas I have 2FA and was able to log in the first time using this code:

from pyicloud import PyiCloudService
api = PyiCloudService(icloud_email, icloud_password)
devices = api.trusted_devices
for i, device in enumerate(devices):
    print('%s: %s' % (i, device.get('deviceName', "SMS to %s" % device.get('phoneNumber'))))
api.send_verification_code(devices[0])

Followed by: api.validate_verification_code(devices[0], verification_code)

But the second time I tried it, I got: PyiCloudAPIResponseError: Missing X-APPLE-WEBAUTH-HSA-LOGIN cookie

I tried uninstalling and reinstalling pyicloud, but that didn't help.

I also tried logging in via the browser from the same machine that I was running the code, in the case that the cookies were being shared with the browser somehow.

Any updates on this issue?

Yes I have successfully done it.

from pyicloud import PyiCloudService api=PyiCloudService('*********@gmail.com','*****') if api.requires_2fa: devices = api.trusted_devices api.send_verification_code(devices[0]) code = input('Please enter validation code:') if not api.validate_verification_code(devices[0], code): print("Failed to verify verification code") exit(1) else: print("Logged In!!!") api.iphone.play_sound() x=input('exit?[y/n]:') if x=='y' : exit(0) else: api.iphone.play_sound()
If you run the code you're supposed to get 2 notifications on your iPhone, one as a popup and the second as an SMS.
Enter the code that comes with the SMS and the program will work.

Since recently 2FA can no longer be disabled, so implementation is highly needed. Can we use https://github.com/ndbroadbent/icloud_photos_downloader implementation? It doesn't require extra coding except accepting auth request on Iphone.

I am using it just fine using the SMS code that is sent. I think this issue can be closed.

leres commented

Since recently 2FA can no longer be disabled, so implementation is highly needed. Can we use https://github.com/ndbroadbent/icloud_photos_downloader implementation? It doesn't require extra coding except accepting auth request on Iphone.

I would sure like to see this.

Why does validate_verification_code() require a specific device? Doesn't iCloud accept the 6-digit verification code from any trusted device?

HI All,

i am using build by @viable-harman
https://github.com/viable-hartman/InflatableDonkey/tree/TwoFAHacky for ios 9 icloud backup but i am getting below error could you please help me out why this is coming

" Exception in thread "main" org.apache.http.client.HttpResponseException: Misdirected Request: {"success":false,"error":"Failed to validate the credentials from cookie"}"

I know that FMFriends has 2fa which works. Would anyone(smarter than me) have a look at the code, and could possible help out with the patch?
https://github.com/Coronon/FMFriends.git

@frejnielsen iCloud3 v1.1.0 has been updated to track devices with Find-My-Friends. I've uploaded the new code to the beta directory on the iCloud3 github repository Go here for the modules (device_tracker.py & pyicloud_ic3.py). The ChangeLog lists the 'breaking changes' and how to set it up.

I want to do some more testing before issuing a formal release (probably next week) so let me know if you see anything weird or it it seems to work fine.

I haven't tested it with multiple accounts being used at the same time but don't expect any issues.
Gary

I have never handled 2FA side although I have spent enormous time on it. On the iOS device, as you might know disabling 2FA is not allowed. I would like to access notes on icloud basically.

I have also tried the patch here. It does not help at all.

I can successfully logged into my account by the help of the branch here. Without this branch, I can not pass icloud login credentials as well. Anyway after handling logging in, it cannot passed the step of api.validate_verification_code, although I have allow it from actual device and enter the 6-digit code into the command prompt once it is asked. Any suggestions ?

I was wondering the status of this 2FA issue?

I am not sure why people still think this is an issue. I am using full 2FA (with the code and map popup on screen) and just authenticated a couple days ago. I really think this issue can be closed.

@kylemcdonald and @mariusciziunas were you able to solve your
pyicloud.exceptions.PyiCloudAPIResponseError: Missing X-APPLE-WEBAUTH-HSA-LOGIN cookie
issue?
I am running into the same problem. I can authenticate and do things once, but it seems some sort of cookie needs to be stored in order to login after the session times out.

With apple currently disabling the option to turn off 2FA, is there a way to use this code without 2FA? I had a similar issue using robinhood's API but I was able to use the backup secret key to skip 2FA. Does apple have a similar option? (something like a secret key which can run without 2FA)

With apple currently disabling the option to turn off 2FA, is there a way to use this code without 2FA? I had a similar issue using robinhood's API but I was able to use the backup secret key to skip 2FA. Does apple have a similar option? (something like a secret key which can run without 2FA)

2SA it's working for you ?
It's necesary to pass all steps of login before using the lib (or accept every time on the popup in your device).

Working on my side.

leres commented

I've been using 2FA with my script for a long time now. I lifted the logic from cmdline.py which checks api.requires_2sa and if necessary (and running interactively) does the verification dance.

In practice when run from cron this triggers about every two months. When that happens I run the script interactively and then it's good for another two months.

cmdline.py also has some nice code for storing the pw in a keyring.

2SA it's working for you ?
It's necesary to pass all steps of login before using the lib (or accept every time on the popup in your device).

Working on my side.

It works only after entering the authentication code which apple sends to me. I'm curious about storing pw in a keyring. I will try the implementation steps in
https://github.com/ndbroadbent/icloud_photos_downloader

leres commented

The keyring code I referred to is in cmdline.py

@leres I found it later that day, it seem to work. However I'm curious on how it stores the 2fa. Without cmdline.py the 2fa was asked every 24 hours.

You can login to icloud.com and get location of your devices without any prompt on your devices. So I think maybe at least find my iPhone service does not need 2FA?

FindMyIphone does not require 2FA for an obvious reason: you use it when you've lost your phone and don't have access to that second factor.

So can we get location without 2FA (2FA enabled) ?

So can we get location without 2FA (2FA enabled) ?

@zeeqy I think you mean without the pin that apple generates every time, if so yes you can.. both status and location of a phone can be retrieved without the need of the pin sent to your phone..

So can we get location without 2FA (2FA enabled) ?

@zeeqy I think you mean without the pin that apple generates every time, if so yes you can.. both status and location of a phone can be retrieved without the need of the pin sent to your phone..

Thanks, but how do we do it through pyicloud? do you have a code snippet?

So can we get location without 2FA (2FA enabled) ?

@zeeqy I think you mean without the pin that apple generates every time, if so yes you can.. both status and location of a phone can be retrieved without the need of the pin sent to your phone..

Thanks, but how do we do it through pyicloud? do you have a code snippet?

@zeeqy This script tracks the location of the phone and triggers an email if out of desired location from AWS SES but basically lines 50-59 has what you're looking for. If you want anything other than status and location you might want to uncomment lines 29-47

https://github.com/thevickypedia/iPhone_Locator/blob/master/locator.py

So can we get location without 2FA (2FA enabled) ?

@zeeqy I think you mean without the pin that apple generates every time, if so yes you can.. both status and location of a phone can be retrieved without the need of the pin sent to your phone..

Thanks, but how do we do it through pyicloud? do you have a code snippet?

@zeeqy This script tracks the location of the phone and triggers an email if out of desired location from AWS SES but basically lines 50-59 has what you're looking for. If you want anything other than status and location you might want to uncomment lines 29-47

https://github.com/thevickypedia/iPhone_Locator/blob/master/locator.py

Thank you! You save my little project hah

I would like to trigger the iPhone find my phone functionality programmatically so my phone will beeping so it can be found. Last I tried this, I was dealing with some cookie issues. Has someone figured out a way to get it to work?

I would like to trigger the iPhone find my phone functionality programmatically so my phone will beeping so it can be found. Last I tried this, I was dealing with some cookie issues. Has someone figured out a way to get it to work?

I don't quite get what you mean by cookie issues. All you need is the play_sound() function. Elaborate your issue if you're looking for something further.

Hi just been playing with this library - works great. Is the issue with 2FA likely to be resolved? It works but times-out after a few hours.

I just set this up and while it appears to be working - things like battery graphs are updated properly. But every half hour I get:

2020-12-08 08:23:49 INFO (SyncWorker_2) [pyicloud.base] Authenticating as user@gmail.com
2020-12-08 08:23:50 INFO (SyncWorker_2) [pyicloud.base] Authentication completed successfully

When this happens I get the notification on my phone that a device is attempting to log in and allow or deny it, and it prompts with the pass code if I allow it, but even if I deny it, which I have been doing for a day now, everything still updates properly.

Any ideas?

@ajjack50n @B3DTech Apple made the 2FA mandatory in new devices so as far as my knowledge there is no way around it. You can try the keychain option but still you need an initial authentication to kick start any script using this lib, it works without needing a 2FA for ~12 hours after which it will prompt for the auth code.

As of now Apple hasn't launched any authenticator (like Duo or Okta) to do this. If they do launch one, then it might be easy to authenticate using a secret bypassing the annoying 2FA by Apple.

@thevickypedia @B3DTech

I am not certain I fully understand. Do not apple use, themselves, the same services to enable their own devices to subscribe to iCloud? Why is it that I do not have to keep authenticating my phone for instance? Is it not not possible to exploit the same integration patterns?

I just set this up and while it appears to be working - things like battery graphs are updated properly. But every half hour I get:

2020-12-08 08:23:49 INFO (SyncWorker_2) [pyicloud.base] Authenticating as user@gmail.com
2020-12-08 08:23:50 INFO (SyncWorker_2) [pyicloud.base] Authentication completed successfully

When this happens I get the notification on my phone that a device is attempting to log in and allow or deny it, and it prompts with the pass code if I allow it, but even if I deny it, which I have been doing for a day now, everything still updates properly.

Any ideas?

You can do nothing about it. I ping my iPhone location every 4 hours. Location service is designed for lost and found, so 2FA is not required since you might not have your device to allow it. But that is a login attempt, it will pop up the notification on your device. As far as I know, there is no way to avoid that annoying allow or deny prompt.

@thevickypedia @B3DTech

I am not certain I fully understand. Do not apple use, themselves, the same services to enable their own devices to subscribe to iCloud? Why is it that I do not have to keep authenticating my phone for instance? Is it not not possible to exploit the same integration patterns?

In layman's terms, it is because your phone has other security measures and it is verified by Apple so it is possible to launch iCloud on your phone without the need of a 2FA, when you use the pyicloud api Apple recognizes the source isn't what your device is so it is natural to kick off a 2FA.
For instance, you can launch iCloud app on an iPhone or iPad or iMac without 2FA but when you use a browser to open iCloud from the same device you will be asked for a verification.
Hope that explains.

Quick update:
A PR has been merged as of January 27, 2021 that brings support for 2FA to pyicloud.
#310

Some work has to be done to merge one other fix and update the documentation, but we are close to closure on this issue.

We will be able to close this with #321

Fantastic news this guys!

@nzapponi - Just saw it pass review on HA/core/dev branch. Congratulations - it's getting closer!