Simplify login sequence
There's no need to validate before authenticating, as we don't log in with extended_login=True, which means there are no persisted login cookies besides the one ensuring we only get a single e-mail. The id that refresh_validate used to generate is also not needed, as authentication with just the username and password works fine, and is what the icloud.com webapp also does. Finally, we can skip the second validate after authentication as the dsInfo/dsid is available through the response we get from the authentication.
This commit is contained in:
parent
3b3857d295
commit
fce7966974
1 changed files with 16 additions and 39 deletions
|
@ -32,7 +32,7 @@ logger = logging.getLogger(__name__)
|
||||||
class PyiCloudService(object):
|
class PyiCloudService(object):
|
||||||
"""
|
"""
|
||||||
A base authentication class for the iCloud service. Handles the
|
A base authentication class for the iCloud service. Handles the
|
||||||
validation and authentication required to access iCloud services.
|
authentication required to access iCloud services.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
from pyicloud import PyiCloudService
|
from pyicloud import PyiCloudService
|
||||||
|
@ -48,7 +48,6 @@ class PyiCloudService(object):
|
||||||
self._setup_endpoint = 'https://setup.icloud.com/setup/ws/1'
|
self._setup_endpoint = 'https://setup.icloud.com/setup/ws/1'
|
||||||
|
|
||||||
self._base_login_url = '%s/login' % self._setup_endpoint
|
self._base_login_url = '%s/login' % self._setup_endpoint
|
||||||
self._base_validate_url = '%s/validate' % self._setup_endpoint
|
|
||||||
|
|
||||||
if cookie_directory:
|
if cookie_directory:
|
||||||
self._cookie_directory = os.path.expanduser(
|
self._cookie_directory = os.path.expanduser(
|
||||||
|
@ -77,64 +76,42 @@ class PyiCloudService(object):
|
||||||
# Most likely a pickled cookiejar from earlier versions
|
# Most likely a pickled cookiejar from earlier versions
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.params = {}
|
self.params = {
|
||||||
|
'clientBuildNumber': '14E45',
|
||||||
|
'clientId': self.client_id,
|
||||||
|
}
|
||||||
|
|
||||||
self.authenticate()
|
self.authenticate()
|
||||||
|
|
||||||
def refresh_validate(self):
|
|
||||||
"""
|
|
||||||
Queries the /validate endpoint and fetches two key values we need:
|
|
||||||
1. "dsInfo" is a nested object which contains the "dsid" integer.
|
|
||||||
This object doesn't exist until *after* the login has taken place,
|
|
||||||
the first request will compain about a X-APPLE-WEBAUTH-TOKEN cookie
|
|
||||||
2. "instance" is an int which is used to build the "id" query string.
|
|
||||||
This is, pseudo: sha1(email + "instance") to uppercase.
|
|
||||||
"""
|
|
||||||
req = self.session.get(self._base_validate_url, params=self.params)
|
|
||||||
resp = req.json()
|
|
||||||
if 'dsInfo' in resp:
|
|
||||||
dsid = resp['dsInfo']['dsid']
|
|
||||||
self.params.update({'dsid': dsid})
|
|
||||||
instance = resp.get(
|
|
||||||
'instance',
|
|
||||||
uuid.uuid4().hex.encode('utf-8')
|
|
||||||
)
|
|
||||||
sha = hashlib.sha1(
|
|
||||||
self.user.get('apple_id').encode('utf-8') + instance
|
|
||||||
)
|
|
||||||
self.params.update({'id': sha.hexdigest().upper()})
|
|
||||||
|
|
||||||
self.params.update({
|
|
||||||
'clientBuildNumber': '14E45',
|
|
||||||
'clientId': self.client_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
def authenticate(self):
|
def authenticate(self):
|
||||||
"""
|
"""
|
||||||
Handles the full authentication steps, validating,
|
Handles authentication, and persists the X-APPLE-WEB-KB cookie so that
|
||||||
authenticating and then validating again.
|
subsequent logins will not cause additional e-mails from Apple.
|
||||||
"""
|
"""
|
||||||
self.refresh_validate()
|
|
||||||
|
|
||||||
data = dict(self.user)
|
data = dict(self.user)
|
||||||
data.update({'id': self.params['id'], 'extended_login': False})
|
|
||||||
|
# We authenticate every time, so "remember me" is not needed
|
||||||
|
data.update({'extended_login': False})
|
||||||
|
|
||||||
req = self.session.post(
|
req = self.session.post(
|
||||||
self._base_login_url,
|
self._base_login_url,
|
||||||
params=self.params,
|
params=self.params,
|
||||||
data=json.dumps(data)
|
data=json.dumps(data)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not req.ok:
|
resp = req.json() if req.ok else {}
|
||||||
|
if 'dsInfo' not in resp:
|
||||||
msg = 'Invalid email/password combination.'
|
msg = 'Invalid email/password combination.'
|
||||||
raise PyiCloudFailedLoginException(msg)
|
raise PyiCloudFailedLoginException(msg)
|
||||||
|
|
||||||
|
self.params.update({'dsid': resp['dsInfo']['dsid']})
|
||||||
|
|
||||||
if not os.path.exists(self._cookie_directory):
|
if not os.path.exists(self._cookie_directory):
|
||||||
os.mkdir(self._cookie_directory)
|
os.mkdir(self._cookie_directory)
|
||||||
self.session.cookies.save()
|
self.session.cookies.save()
|
||||||
|
|
||||||
self.refresh_validate()
|
self.discovery = resp
|
||||||
|
|
||||||
self.discovery = req.json()
|
|
||||||
self.webservices = self.discovery['webservices']
|
self.webservices = self.discovery['webservices']
|
||||||
|
|
||||||
def _get_cookiejar_path(self):
|
def _get_cookiejar_path(self):
|
||||||
|
|
Loading…
Reference in a new issue