Compare commits
7 Commits
1744e3bbdf
...
127774a1de
Author | SHA1 | Date | |
---|---|---|---|
127774a1de | |||
ea8729bbaa | |||
7929032313 | |||
|
d53ed1d58e | ||
|
224658af0d | ||
|
d9c018fc44 | ||
|
bcc006fe02 |
16
README.md
16
README.md
@ -44,10 +44,18 @@ Run `icloud` program a first time to ensure everything is running smooth:
|
|||||||
|
|
||||||
### Using 2FA
|
### Using 2FA
|
||||||
|
|
||||||
If you enabled 2FA on your Apple iCloud account, the script will run a simple
|
If you enabled 2FA on your Apple iCloud account, you can request the 2FA code
|
||||||
webserver and point you to the URL on localhost to pass it the 2FA code you
|
beforehand by :
|
||||||
will get from one of your iDevices. This makes the script trustable for some
|
If your device is online:
|
||||||
time and this operation is only required once in a while (every month or so).
|
Go to Settings > [your name].
|
||||||
|
Tap Sign-In & Security > Two Factor Authentication.
|
||||||
|
Tap Get Verification Code
|
||||||
|
If your device is offline:
|
||||||
|
Go to Settings > [your name].
|
||||||
|
Tap Sign-In & Security.
|
||||||
|
A message says "Account Details Unavailable." Tap Get Verification Code.
|
||||||
|
Put this 2FA code in the config.ini file, you have one minute to run the command line in the next section.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
[apple]
|
[apple]
|
||||||
email = apple_icloud_email
|
email = apple_icloud_email
|
||||||
password = apple_icloud_password
|
password = apple_icloud_password
|
||||||
|
code_2fa = 2fa_code
|
||||||
|
iPhone_name = iPhone
|
||||||
cookie_directory = COOKIE_DIRECTORY
|
cookie_directory = COOKIE_DIRECTORY
|
||||||
|
|
||||||
[nextcloud]
|
[nextcloud]
|
||||||
server = https://cloud.example.com
|
server = https://cloud.example.com
|
||||||
user = nextcloud_user
|
user = nextcloud_user
|
||||||
password = nextcloud_password
|
password = nextcloud_password
|
||||||
|
|
||||||
[webserver]
|
|
||||||
host = localhost
|
|
||||||
port = 8080
|
|
||||||
|
@ -4,78 +4,12 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
import bottle
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from pyicloud import PyiCloudService
|
from pyicloud import PyiCloudService
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
|
|
||||||
class StoppableCherootServer(bottle.ServerAdapter):
|
|
||||||
"""
|
|
||||||
We need a stoppable HTTP server, which can be stopped from within a route.
|
|
||||||
|
|
||||||
This is not doable out of the box in bottle and is quite hacky using plain
|
|
||||||
WSGIRef. This is easier and cleaner with Cheroot (formally CherryPy)
|
|
||||||
backend.
|
|
||||||
"""
|
|
||||||
def run(self, handler): # pragma: no cover
|
|
||||||
from cheroot import wsgi
|
|
||||||
self.options['bind_addr'] = (self.host, self.port)
|
|
||||||
self.options['wsgi_app'] = handler
|
|
||||||
self.server = wsgi.Server(**self.options)
|
|
||||||
try:
|
|
||||||
self.server.start()
|
|
||||||
finally:
|
|
||||||
self.server.stop()
|
|
||||||
|
|
||||||
|
|
||||||
############################################
|
|
||||||
# Web app to fetch 2FA code from the user. #
|
|
||||||
############################################
|
|
||||||
|
|
||||||
code_2fa = None # Global for passing 2FA code from web app to main script
|
|
||||||
app = bottle.Bottle()
|
|
||||||
server = None
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def get_2fa():
|
|
||||||
"""
|
|
||||||
Main HTTP route, display an HTML form to fetch 2FA code from user.
|
|
||||||
"""
|
|
||||||
return """
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="fr">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>iCloud 2FA protection</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<form method="post" action="/2fa">
|
|
||||||
<p>
|
|
||||||
<label for="2FA">2FA password?</label>
|
|
||||||
<input type="text" id="2FA" name="2FA"/>
|
|
||||||
</p>
|
|
||||||
<input type="submit"/>
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>"""
|
|
||||||
|
|
||||||
|
|
||||||
@app.post('/2fa')
|
|
||||||
def set_2fa():
|
|
||||||
"""
|
|
||||||
Handle form submission and store 2FA code to pass along the rest of the
|
|
||||||
code.
|
|
||||||
"""
|
|
||||||
global code_2fa
|
|
||||||
global server
|
|
||||||
code_2fa = bottle.request.forms.get('2FA')
|
|
||||||
server.server.stop()
|
|
||||||
return "OK"
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Main script #
|
# Main script #
|
||||||
###############
|
###############
|
||||||
@ -95,23 +29,16 @@ def get_icloud_location(config):
|
|||||||
"""
|
"""
|
||||||
Fetch latest iPhone location from iCloud
|
Fetch latest iPhone location from iCloud
|
||||||
"""
|
"""
|
||||||
global server
|
|
||||||
global code_2fa
|
|
||||||
email = config['apple']['email']
|
email = config['apple']['email']
|
||||||
password = config['apple']['password']
|
password = config['apple']['password']
|
||||||
|
code_2fa = config['apple']['code_2fa']
|
||||||
|
|
||||||
api = PyiCloudService(email, password=password, cookie_directory=config['apple']['cookie_directory'])
|
api = PyiCloudService(email, password=password, cookie_directory=config['apple']['cookie_directory'])
|
||||||
|
|
||||||
if api.requires_2fa:
|
if api.requires_2fa:
|
||||||
print(
|
print("Two-factor authentication required.")
|
||||||
"Two-factor authentication required. "
|
code = code_2fa
|
||||||
f"Head over to http://{config['webserver']['host']}:{config['webserver']['port']} and fill-in the 2FA code."
|
result = api.validate_2fa_code(code)
|
||||||
)
|
|
||||||
server = StoppableCherootServer(
|
|
||||||
host=config['webserver']['host'],
|
|
||||||
port=int(config['webserver']['port'])
|
|
||||||
)
|
|
||||||
app.run(server=server)
|
|
||||||
result = api.validate_2fa_code(code_2fa)
|
|
||||||
print("Code validation result: %s" % result)
|
print("Code validation result: %s" % result)
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
@ -124,11 +51,7 @@ def get_icloud_location(config):
|
|||||||
print("Session trust result %s" % result)
|
print("Session trust result %s" % result)
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
print(
|
print("Failed to request trust. You will likely be prompted for the code again in the coming weeks")
|
||||||
"Failed to request trust. "
|
|
||||||
"You will likely be prompted for the code again "
|
|
||||||
"in the coming weeks"
|
|
||||||
)
|
|
||||||
elif api.requires_2sa:
|
elif api.requires_2sa:
|
||||||
import click
|
import click
|
||||||
print("Two-step authentication required. Your trusted devices are:")
|
print("Two-step authentication required. Your trusted devices are:")
|
||||||
@ -136,11 +59,8 @@ def get_icloud_location(config):
|
|||||||
devices = api.trusted_devices
|
devices = api.trusted_devices
|
||||||
for i, device in enumerate(devices):
|
for i, device in enumerate(devices):
|
||||||
print(
|
print(
|
||||||
" %s: %s" % (
|
" %s: %s" % (i, device.get('deviceName',
|
||||||
i, device.get(
|
"SMS to %s" % device.get('phoneNumber')))
|
||||||
'deviceName', "SMS to %s" % device.get('phoneNumber')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
device = click.prompt('Which device would you like to use?', default=0)
|
device = click.prompt('Which device would you like to use?', default=0)
|
||||||
@ -155,10 +75,10 @@ def get_icloud_location(config):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
iphone = next(
|
iphone = next(
|
||||||
device
|
device
|
||||||
for device in api.devices
|
for device in api.devices
|
||||||
if 'iPhone' in device.status()['name']
|
if config['apple']['iPhone_name'] in device.status()['name']
|
||||||
)
|
)
|
||||||
iphone_location = iphone.location()
|
iphone_location = iphone.location()
|
||||||
iphone_status = iphone.status()
|
iphone_status = iphone.status()
|
||||||
|
|
||||||
|
@ -1,3 +1 @@
|
|||||||
bottle
|
|
||||||
cheroot
|
|
||||||
pyicloud
|
pyicloud
|
||||||
|
Loading…
Reference in New Issue
Block a user