malmeloo / FindMy.py

🍏 + 🎯 + 🐍 = Everything you need to work with Apple's FindMy network!
http://docs.mikealmel.ooo/FindMy.py/
MIT License
59 stars 7 forks source link

Trusted device 2FA #1

Closed malmeloo closed 4 months ago

malmeloo commented 6 months ago

Support trusted device 2FA. Need someone with an Apple device to test this for me, so please let me know if you're willing to help!

YeapGuy commented 5 months ago

I have an experimental iPhone and a Hackintosh, so I'm happy to help with this. :smiley: I just tried to get the location reports for the key I retrieved thanks to your work in #4, but I can't log in to my Apple ID:

findmy.util.errors.UnhandledProtocolError: Unknown auth value: trustedDeviceSecondaryAuth
malmeloo commented 5 months ago

That would be great! It shouldn't be too difficult to implement, so I'll try to add it soon. Unfortunately I don't think apple allows SMS 2FA when you have a trusted device, which is why you cannot log in right now.

malmeloo commented 4 months ago

Trusted device 2FA support has been added in #9. I tried to piggyback off of the existing SMS 2FA code so the HTTP headers aren't entirely correct; I think it should still work, but if it doesn't let me know.

YeapGuy commented 4 months ago

Doesn't work :/

INFO:root:Using remote anisette server: http://localhost:6969
DEBUG:asyncio:Using selector: EpollSelector
INFO:root:Attempting authentication for user apple.prim@alias.tailmail.eu
DEBUG:root:Starting authentication with username
DEBUG:root:Creating aiohttp session
DEBUG:root:Creating aiohttp session
DEBUG:root:Verifying response to auth request
DEBUG:root:Attempting password challenge
DEBUG:root:Verifying password challenge response
DEBUG:root:Decrypting SPD data in response
DEBUG:root:Received account information
INFO:root:Detected 2FA requirement: trustedDeviceSecondaryAuth
INFO:root:Transitioning login state: LoginState.LOGGED_OUT -> LoginState.REQUIRE_2FA
0 - Trusted Device
1 - SMS (<phone number>)
Method? > 0
Traceback (most recent call last):
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 61, in fetch_reports
    with acc_store.open() as f:
  File "/usr/lib/python3.10/pathlib.py", line 1119, in open
    return self._accessor.open(self, mode, buffering, encoding, errors,
FileNotFoundError: [Errno 2] No such file or directory: 'account.json'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 81, in <module>
    fetch_reports(key)
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 64, in fetch_reports
    login(acc)
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 47, in login
    method.request()
  File "/home/matus/git/FindMy.py/findmy/reports/twofactor.py", line 231, in request
    return self.account.td_2fa_request()
  File "/home/matus/git/FindMy.py/findmy/reports/account.py", line 888, in td_2fa_request
    return self._evt_loop.run_until_complete(coro)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/home/matus/git/FindMy.py/findmy/reports/account.py", line 488, in td_2fa_request
    await self._sms_2fa_request(
  File "/home/matus/git/FindMy.py/findmy/reports/account.py", line 736, in _sms_2fa_request
    raise UnhandledProtocolError(msg)
findmy.errors.UnhandledProtocolError: SMS 2FA request failed: 403
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7919597444c0>
ERROR:asyncio:Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x791959707220>, 1950.037792264)]']
connector: <aiohttp.connector.TCPConnector object at 0x791959744460>
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7919597445b0>
ERROR:asyncio:Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x79195976d300>, 1950.202662598)]']
connector: <aiohttp.connector.TCPConnector object at 0x791959744610>

Seems like it's trying to do an SMS 2FA request..? But when I selected SMS, that worked :)

malmeloo commented 4 months ago

Hmm. The request was missing some headers which I didn't think were too important, so I've just added them. Can you try again?

The trusted device code uses the same method as the sms 2FA code, and I haven't renamed it yet which is why it looks like it's doing SMS 2FA. I'm planning on refactoring most of the anisette header generation there, so it will probably be gone soon anyway.

YeapGuy commented 4 months ago

Now Apple apparently trusts me without 2FA 😄 (even on a different network), so I can't test it...

malmeloo commented 4 months ago

Try using a different Anisette server, that should force re-auth. If you're running one locally you can also just clear its storage and restart it.

YeapGuy commented 4 months ago

Right. It still doesn't work unfortunately. Ends with a 403;

INFO:root:Using remote anisette server: http://localhost:6969
DEBUG:asyncio:Using selector: EpollSelector
INFO:root:Attempting authentication for user apple.prim@alias.tailmail.eu
DEBUG:root:Starting authentication with username
DEBUG:root:Creating aiohttp session
DEBUG:root:Creating aiohttp session
DEBUG:root:Verifying response to auth request
DEBUG:root:Attempting password challenge
DEBUG:root:Verifying password challenge response
DEBUG:root:Decrypting SPD data in response
DEBUG:root:Received account information
INFO:root:Detected 2FA requirement: trustedDeviceSecondaryAuth
INFO:root:Transitioning login state: LoginState.LOGGED_OUT -> LoginState.REQUIRE_2FA
<phone number>
Traceback (most recent call last):
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 57, in fetch_reports
    with acc_store.open() as f:
  File "/usr/lib/python3.10/pathlib.py", line 1119, in open
    return self._accessor.open(self, mode, buffering, encoding, errors,
FileNotFoundError: [Errno 2] No such file or directory: 'account.json'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 76, in <module>
    fetch_reports(key)
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 60, in fetch_reports
    login(acc)
  File "/home/matus/git/FindMy.py/fetch_reports.py", line 43, in login
    method.request()
  File "/home/matus/git/FindMy.py/findmy/reports/twofactor.py", line 231, in request
    return self.account.td_2fa_request()
  File "/home/matus/git/FindMy.py/findmy/reports/account.py", line 897, in td_2fa_request
    return self._evt_loop.run_until_complete(coro)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/home/matus/git/FindMy.py/findmy/reports/account.py", line 492, in td_2fa_request
    await self._sms_2fa_request(
  File "/home/matus/git/FindMy.py/findmy/reports/account.py", line 745, in _sms_2fa_request
    raise UnhandledProtocolError(msg)
findmy.errors.UnhandledProtocolError: SMS 2FA request failed: 403
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7e24e8ec4100>
ERROR:asyncio:Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x7e24e8e83160>, 2121.062402681)]']
connector: <aiohttp.connector.TCPConnector object at 0x7e24e8ec40a0>
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7e24e8e6ffd0>
ERROR:asyncio:Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x7e24e8ce9240>, 2121.456896539)]']
connector: <aiohttp.connector.TCPConnector object at 0x7e24e8ec4df0>
malmeloo commented 4 months ago

I just found out that I can call that endpoint for my device-less account and it will just... request an SMS code. Lol. Anyway, it should be fixed now :-)

YeapGuy commented 3 months ago

Sorry, just now I had time to test it out. I can confirm all works perfectly now! One just has to be pretty fast to enter the 2FA code, or it times out and throws at first sight a quite cryptic error (findmy.errors.UnhandledProtocolError: SMS 2FA request failed: 434)

malmeloo commented 3 months ago

No worries, I got a friend to test it for me. That's... an interesting error code, to say the least; I don't think 434 is a standard HTTP status code. With "pretty fast," what rough time frame are you referring to? It might be an apple-enforced thing, in which case it's just a matter of better error management. It'd be up to the library implementer to actually try again.

YeapGuy commented 3 months ago

It took me a minute or two to enter the code, and I got that error. So not too long. If you want to know, I can spend some time figuring out how long the timeout is exactly. But yeah, you're right that this is the job of the code that implements the library.

malmeloo commented 3 months ago

I don't think it's too important, but if you really want feel free to experiment with it. My assumption is that this will also occur for SMS 2FA though, but I've never taken that long to enter the code, so I cannot tell from experience. Think if the code just tests for that status code and raises an exception it should be sufficient.