I'm playing around with iLoot, an open source app that let's you download iCloud backups and I was wondering how it is possible to implement the two factor authentication to it.
I have 2fa enabled on my account and what I get on the first request is this:
First request:
auth = "Basic %s" % base64.b64encode("%s:%s" % (login, password))
authenticateResponse = plist_request("setup.icloud.com", "POST", "/setup/authenticate/$APPLE_ID$", "", {"Authorization": auth})
plist_request is just a normal python (request) function that request from the url and returns the parsed xml.
First response (in xml format):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>protocolVersion</key>
<string>2</string>
<key>title</key>
<string>Verification Required</string>
<key>localizedError</key>
<string>MOBILEME_TERMS_OF_SERVICE_UPDATE</string>
<key>message</key>
<string>This Apple ID is protected with two-step verification. To sign in, you must verify your identity.</string>
</dict>
</plist>
Request /setup/authenticate/$APPLE_ID$ returned code 409
In case anyone knows what the next call would be to input the two-step verification code, it could help to figure it out.
I'm an iOS guy, not backend guy, but my backend team solved this on their side by using the app-specific password, instead of tokens.
This is how user can generate an app-specific password: Generate App-Specific Password
Then simply use the email and this password, instead of the token formed by dsPrsID:mmeAuthToken. As I learnt for my teammates, you don't have to do base64 encode, just use that new password, and the url:'https://caldav.icloud.com/'
Hope this helps. It was a nightmare for us, no help at all on the internet.
UPDATE
For more info, these are the steps I used on my side (iOS):
I call https://setup.icloud.com/setup/authenticate/"[email protected]", by setting "Basic email:password" as base64 string to "Authorization" http header field.
I check response status code. If 401, it means the password you are sending is an app specific password, so then I send this password (and email) directly to my server, via our internal API, and they are using it to call iCloud APIs, and return me calendars, etc.
If response status code is 409, it means that user tried to login with a normal password, but his account is enabled for 2fa, and I display an error, warning user to go and retrieve his app-specific-password, and use that one, which will lead to step 2, above.
I am not saying this is the only way, but this worked for me. Also, I don't exactly know what's going on on server side, but they are using directly that app-specific-passwrod (and probably the email), to do the job.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With