|
1 | 1 | # Logging in |
2 | 2 |
|
3 | | -Some useful features of this library require an active login session with Apple in order to work correctly. |
| 3 | +Most useful features of this library require an active login session with Apple in order to work correctly. |
4 | 4 | The reason for this is that the remote endpoints require authentication to actually retrieve data. |
5 | 5 | This page will guide you through the steps needed to log into an Apple account using FindMy.py. |
6 | 6 |
|
| 7 | +## Step 0: Account Requirements |
7 | 8 |
|
| 9 | +FindMy.py requires an **active** Apple Account which has had a device attached to it **at least once**. |
| 10 | +It is OK if there are currently no devices signed into the account, as long as a device has signed into |
| 11 | +it at least once in the past. Note that this does not have to be a _real_ device: a hackintosh using e.g. |
| 12 | +[Docker-OSX](https://github.com/sickcodes/Docker-OSX) may also work for you if the VM is configured correctly. |
| 13 | +We do not and will not provide support regarding setting this up. |
| 14 | + |
| 15 | +Additionally, if you want to track your AirTags, iDevices or other FindMy-compatible 3rd party devices, |
| 16 | +the account used for FindMy.py does _not_ have to be the same one as the one that the devices are attached to. |
| 17 | +Given the right decryption keys, any Apple account can query the location history of any FindMy device. |
| 18 | +However, if you want to track such an official device, you currently must have access to a machine that is |
| 19 | +running a compatible version of MacOS in order to extract the decryption keys (see later sections). |
| 20 | + |
| 21 | +## Step 1: Creating an AppleAccount instance |
| 22 | + |
| 23 | +The first time we want to sign in, we must manually construct an instance of the [AppleAccount](#findmy.AppleAccount) |
| 24 | +class. Creating such a class requires specifying an [Anisette](../technical/15-Anisette.md) provider. Anisette |
| 25 | +data is usually generated on-device, and identifies our virtual device when we make a request to Apple's servers. |
| 26 | + |
| 27 | +There are two different Anisette providers included in FindMy.py: [LocalAnisetteProvider](#findmy.LocalAnisetteProvider) |
| 28 | +and [RemoteAnisetteProvider](#findmy.RemoteAnisetteProvider). The local provider is much easier to use, |
| 29 | +so we will be utilizing it in this example. |
| 30 | + |
| 31 | +```python |
| 32 | +from findmy import AppleAccount, LocalAnisetteProvider |
| 33 | + |
| 34 | +ani = LocalAnisetteProvider(libs_path="ani_libs.bin") |
| 35 | +account = AppleAccount(ani) |
| 36 | +``` |
| 37 | + |
| 38 | +Note the `libs_path` argument: the local Anisette provider needs to use some proprietary libraries |
| 39 | +from Apple, which will be stored in this file. They will be automatically downloaded if the file is missing. |
| 40 | +While the argument is technically optional, it is highly recommended to provide it; otherwise, the library |
| 41 | +will need to re-download the bundle every time. The size of the bundle is approximately 2,1 MB. |
| 42 | + |
| 43 | +## Step 2: Logging in |
| 44 | + |
| 45 | +Logging into an Apple Account is an interactive process: depending on the circumstances, 2FA may or may |
| 46 | +not be required, and there are multiple different methods to perform 2FA authentication. FindMy.py supports |
| 47 | +both SMS and Trusted Device challenges to pass the 2FA check, but you must handle the sign-in flow manually in your application. |
| 48 | + |
| 49 | +```{attention} |
| 50 | +FindMy.py currently does not support passkey authentication: [#159](https://github.com/malmeloo/FindMy.py/issues/159). |
| 51 | +If you use a passkey to secure your Apple Account, you must disable it to use FindMy.py. This is because enabling |
| 52 | +passkeys for your account will disable other 2FA mechanisms. |
| 53 | +``` |
| 54 | + |
| 55 | +To start the authentication process, provide your email and password as follows: |
| 56 | + |
| 57 | +```python |
| 58 | +state = account.login(email, password) |
| 59 | +``` |
| 60 | + |
| 61 | +The `state` variable will now contain a [LoginState](#findmy.LoginState). If `value == LoginState.LOGGED_IN`, you're |
| 62 | +good! Continue to the next step. If `value == LoginState.REQUIRE_2FA`, we need to pass a 2FA challenge first. |
| 63 | +Read on to learn how to do this. |
| 64 | + |
| 65 | +In order to pass the 2FA challenge, we first need to find out which challenges Apple provides to us. We can use either |
| 66 | +one of these challenges to continue the login flow. |
| 67 | + |
| 68 | +```python |
| 69 | +from findmy import LoginState, TrustedDeviceSecondFactorMethod, SmsSecondFactorMethod |
| 70 | + |
| 71 | +if state == LoginState.REQUIRE_2FA: # Account requires 2FA |
| 72 | + methods = account.get_2fa_methods() |
| 73 | + |
| 74 | + for i, method in enumerate(methods): |
| 75 | + if isinstance(method, TrustedDeviceSecondFactorMethod): |
| 76 | + print(f"{i} - Trusted Device") |
| 77 | + elif isinstance(method, SmsSecondFactorMethod): |
| 78 | + print(f"{i} - SMS ({method.phone_number})") |
| 79 | + |
| 80 | + # example output: |
| 81 | + # 0 - Trusted Device |
| 82 | + # 1 - SMS (+31 •• ••••••55) |
| 83 | + # 2 - SMS (+31 •• ••••••32) |
| 84 | +``` |
| 85 | + |
| 86 | +Depending on your account configuration, you will either get more or fewer 2FA challenge options. |
| 87 | +In order to pass one of these challenges, we will first call its `request()` method to request a code |
| 88 | +(on a Trusted Device or via SMS), and then use the `submit()` method to submit the code and pass the challenge. |
| 89 | + |
| 90 | +```python |
| 91 | + ind = int(input("Method? > ")) |
| 92 | + |
| 93 | + method = methods[ind] |
| 94 | + method.request() |
| 95 | + code = input("Code? > ") |
| 96 | + |
| 97 | + method.submit(code) |
| 98 | +``` |
| 99 | + |
| 100 | +If all went well, you should now be logged in! |
| 101 | + |
| 102 | +## Step 3: Saving / restoring the session |
| 103 | + |
| 104 | +Before we continue to fetching device locations, I first want to talk about properly closing and restoring sessions. |
| 105 | +Apple Account sessions are precious, and you shall not create more of them than necessary. Each time we go through the |
| 106 | +steps outlined above, a new 'device' is added to your account, and you will need to go through the 2FA flow again. |
| 107 | +This is inefficient and simply unnecessary. |
| 108 | + |
| 109 | +Therefore, once you are done, it is good practice to save the current state of the account to a file, as well as close |
| 110 | +any resources that the instance may be holding onto: |
| 111 | + |
| 112 | +```python |
| 113 | +acc.to_json("account.json") |
| 114 | + |
| 115 | +acc.close() |
| 116 | +``` |
| 117 | + |
| 118 | +Then, if you want to pick up the session again later: |
| 119 | + |
| 120 | +```python |
| 121 | +acc = AppleAccount.from_json("account.json", anisette_libs_path="ani_libs.bin") |
| 122 | +``` |
0 commit comments